Repository: scionproto/scion
Pull request #3792 (2020-06-17 21:03:00)

cs/sd: implement new path lookup strategy
Remove segment synchronization and adapt the path lookup strategy accordingly

1. During registration, a non-core AS only registers down-segments with the core AS they originate from.
2. Each core AS only has the down-segments for which it received an explicit registration.
3. During path lookup, a requester asks each core AS for which it wants to include down-segments to a non-core AS. By default, a requester asks all core ASes of an ISD.

Furthemore, the down-segment requests in remote ISDs where previously handled by a core AS of the local ISD, forwarding and caching the request. This intermediary has now been removed, down-segment requests are sent directly to core AS in the remote ISD.

There are two distinct segment request handlers:
* authoritative: returns core- and down-segments registered with this core AS -- this is a trivial DB lookup
* forwarder: handles requests from hosts in the local AS (TCP only). This forwards and caches core- and down-segment requests to the authoritative core AS (i.e. the AS at the start of the segment). Up-segment requests are always answered from DB.
This handler expands wildcard requests to a request to each authoritative core AS. In order to forward requests to an authoritative core AS, an additional (recursive) request for the corresponding core segments is made.

Further documentation will be added in https://github.com/scionproto/scion/pull/3747.

#### Notes

* a single path request can now entail multiple down-segment requests (one for each core AS). The `segfetcher.RequestSet` data structure did not allow for this. Thus it was replaced with a simpler flat list of typed segment requests. Analogously, `segfetcher.Segments` is now a flat list of typed segments instead of one separate list of segments per type.
* the `segfetcher.Resolver` can now be dumber because it no longer needs to keep track of the order in which segments of different can be fetched. The state machine of the requests in the `Fetcher` and `Resolver` is now trivial, segments are either loaded from the DB or else fetched. This is possible because of the aforementioned "recursive" use of the `segfetcher.Fetcher` and (thus the `segfetcher.Resolver`) for a core-segment lookup when forwarding down-segment requests.
* the `segfetcher` module is now much dumber/simpler overall. This is reflected with a somewhat reduced set of test cases for the `segfetcher.Resolver`
* the `segfetcher.Splitter` which splits a path request into segment requests is no longer part of the `Fetcher`, but only of the `Pather` (one abstraction level higher). This allows to re-use the simplifed `Fetcher` for the implementation of the forwarding segment request handler.
* The lookup strategy employed by sciond is to make (up to three) independent, concurrent wildcard requests (by means of the `segfetcher.Pather` mentioned above). Assuming everything is cached in the local path server, this finds all paths with minimal latency. Other strategies, such as a depth-first-search that could perhaps find a single path more quickly could be implemented without further changes to the protocol.

Contributes to #3731 

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/scionproto/scion/3792)
<!-- Reviewable:end -->



Patch:
diff --git a/go/cs/BUILD.bazel b/go/cs/BUILD.bazel
index 8f6b8bcac..4080960f1 100644
--- a/go/cs/BUILD.bazel
+++ b/go/cs/BUILD.bazel
@@ -30,7 +30,6 @@ go_library(
         "//go/lib/infra/infraenv:go_default_library",
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/segfetcher:go_default_library",
         "//go/lib/infra/modules/seghandler:go_default_library",
         "//go/lib/log:go_default_library",
         "//go/lib/pathdb:go_default_library",
diff --git a/go/cs/beaconing/BUILD.bazel b/go/cs/beaconing/BUILD.bazel
index 27bee06f8..787397ecb 100644
--- a/go/cs/beaconing/BUILD.bazel
+++ b/go/cs/beaconing/BUILD.bazel
@@ -34,6 +34,7 @@ go_library(
         "//go/lib/slayers/path:go_default_library",
         "//go/lib/slayers/path/scion:go_default_library",
         "//go/lib/snet:go_default_library",
+        "//go/lib/snet/addrutil:go_default_library",
         "//go/lib/spath:go_default_library",
         "//go/lib/topology:go_default_library",
         "//go/lib/util:go_default_library",
diff --git a/go/cs/beaconing/registrar.go b/go/cs/beaconing/registrar.go
index 9124f4480..0d9955501 100644
--- a/go/cs/beaconing/registrar.go
+++ b/go/cs/beaconing/registrar.go
@@ -30,6 +30,7 @@ import (
 	"github.com/scionproto/scion/go/lib/infra/modules/seghandler"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/periodic"
+	"github.com/scionproto/scion/go/lib/snet/addrutil"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/proto"
 )
@@ -50,11 +51,6 @@ type RPC interface {
 	RegisterSegment(ctx context.Context, meta seg.Meta, remote net.Addr) error
 }
 
-// Pather computes the remote address with a path based on the provided segment.
-type Pather interface {
-	GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error)
-}
-
 var _ periodic.Task = (*Registrar)(nil)
 
 // Registrar is used to periodically register path segments with the appropriate
@@ -65,7 +61,7 @@ type Registrar struct {
 	Provider SegmentProvider
 	Store    SegmentStore
 	RPC      RPC
-	Pather   Pather
+	Pather   addrutil.Pather
 	IA       addr.IA
 	Signer   ctrl.Signer
 	Intfs    *ifstate.Interfaces
@@ -215,7 +211,7 @@ func (r *Registrar) logSummary(logger log.Logger, s *summary) {
 type remoteRegistrar struct {
 	segType proto.PathSegType
 	rpc     RPC
-	pather  Pather
+	pather  addrutil.Pather
 	summary *summary
 	wg      *sync.WaitGroup
 }
diff --git a/go/cs/cs.go b/go/cs/cs.go
index 343a5044a..e622b7530 100644
--- a/go/cs/cs.go
+++ b/go/cs/cs.go
@@ -41,7 +41,6 @@ import (
 	"github.com/scionproto/scion/go/lib/infra/infraenv"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/infra/modules/seghandler"
 	"github.com/scionproto/scion/go/lib/log"
 	"github.com/scionproto/scion/go/lib/pathdb"
@@ -166,29 +165,13 @@ func run(file string) error {
 	}
 	defer beaconStore.Close()
 
-	pather := cs.NewPather(itopo.Provider(), cfg.Features.HeaderV2)
 	inspector := trust.DBInspector{DB: trustDB}
-	var dstProvider segfetcher.DstProvider
-	if topo.Core() {
-		dstProvider = segreq.CreateCoreDstProvider(topo.IA(), pather, pathDB, revCache)
-	} else {
-		dstProvider = segreq.CreateNonCoreDstProvider(topo.IA(), pather, pathDB, revCache,
-			inspector)
-	}
 	provider := cs.NewTrustProvider(
 		cs.TrustProviderConfig{
-			IA:            topo.IA(),
-			PathDB:        pathDB,
-			RevCache:      revCache,
-			TrustDB:       trustDB,
-			RPC:           msgr,
-			Pather:        pather,
-			Inspector:     inspector,
-			Provider:      itopo.Provider(),
-			DstProvider:   dstProvider,
-			QueryInterval: cfg.PS.QueryInterval.Duration,
-			Core:          topo.Core(),
-			HeaderV2:      cfg.Features.HeaderV2,
+			IA:       topo.IA(),
+			TrustDB:  trustDB,
+			RPC:      msgr,
+			HeaderV2: cfg.Features.HeaderV2,
 		},
 	)
 	verifier := compat.Verifier{
@@ -196,6 +179,17 @@ func run(file string) error {
 			Engine: provider,
 		},
 	}
+	fetcherCfg := segreq.FetcherConfig{
+		IA:           topo.IA(),
+		PathDB:       pathDB,
+		RevCache:     revCache,
+		RequestAPI:   msgr,
+		Inspector:    inspector,
+		TopoProvider: itopo.Provider(),
+		Verifier:     verifier,
+		HeaderV2:     cfg.Features.HeaderV2,
+	}
+	cs.SetTrustRouter(&provider, segreq.NewRouter(fetcherCfg))
 
 	// Register trust material related handlers.
 	trcHandler := trusthandler.TRCReq{Provider: provider, IA: topo.IA()}
@@ -205,22 +199,24 @@ func run(file string) error {
 
 	// Register pathing related handlers
 	msgr.AddHandler(infra.Seg, beaconing.NewHandler(topo.IA(), intfs, beaconStore, verifier))
-	cs.MultiRegister(infra.SegRequest, &segreq.Handler{
-		Fetcher: segfetcher.FetcherConfig{
-			QueryInterval:    cfg.PS.QueryInterval.Duration,
-			LocalIA:          topo.IA(),
-			Verifier:         verifier,
-			PathDB:           pathDB,
-			RevCache:         revCache,
-			RequestAPI:       msgr,
-			DstProvider:      dstProvider,
-			Splitter:         &segreq.Splitter{ASInspector: inspector},
-			MetricsNamespace: metrics.PSNamespace,
-			LocalInfo:        segreq.CreateLocalInfo(topo.Core(), topo.IA(), inspector),
-		}.New(),
-		RevCache: revCache,
-	}, msgr, tcpMsgr)
+
+	tcpMsgr.AddHandler(infra.SegRequest, segreq.NewForwardingHandler(
+		topo.IA(),
+		topo.Core(),
+		inspector,
+		pathDB,
+		revCache,
+		segreq.NewFetcher(fetcherCfg),
+	))
+
 	if topo.Core() {
+		msgr.AddHandler(infra.SegRequest, segreq.NewAuthoritativeHandler(
+			topo.IA(),
+			inspector,
+			pathDB,
+			revCache,
+		))
+
 		segHandler := seghandler.Handler{
 			Verifier: &seghandler.DefaultVerifier{
 				Verifier: verifier,
@@ -231,8 +227,6 @@ func run(file string) error {
 			},
 		}
 		msgr.AddHandler(infra.SegReg, &handlers.SegReg{SegHandler: segHandler})
-		// Legacy down segment synchronization mechanism.
-		msgr.AddHandler(infra.SegSync, &handlers.SegSync{SegHandler: segHandler})
 	}
 
 	// Keepalive mechanism is deprecated and will be removed with change to
diff --git a/go/cs/handlers/BUILD.bazel b/go/cs/handlers/BUILD.bazel
index cfdf79250..89de3b7f7 100644
--- a/go/cs/handlers/BUILD.bazel
+++ b/go/cs/handlers/BUILD.bazel
@@ -6,7 +6,6 @@ go_library(
         "log.go",
         "segreg.go",
         "segrevoc.go",
-        "segsync.go",
     ],
     importpath = "github.com/scionproto/scion/go/cs/handlers",
     visibility = ["//visibility:public"],
diff --git a/go/cs/handlers/segsync.go b/go/cs/handlers/segsync.go
deleted file mode 100644
index 66518cfec..000000000
--- a/go/cs/handlers/segsync.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package handlers
-
-import (
-	"github.com/scionproto/scion/go/cs/metrics"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/infra/modules/seghandler"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/snet"
-	"github.com/scionproto/scion/go/proto"
-)
-
-// SegSync handles down segment synchronization.
-type SegSync struct {
-	SegHandler seghandler.Handler
-}
-
-// Handle handles a down segment synchronization message.
-func (h *SegSync) Handle(request *infra.Request) *infra.HandlerResult {
-	ctx := request.Context()
-	logger := log.FromCtx(ctx)
-	labels := metrics.SyncRegLabels{
-		Result: metrics.ErrInternal,
-	}
-	segSync, ok := request.Message.(*path_mgmt.SegSync)
-	if !ok {
-		logger.Error("[syncHandler] wrong message type, expected path_mgmt.SegSync",
-			"msg", request.Message, "type", common.TypeOf(request.Message))
-		metrics.Sync.Registrations(labels).Inc()
-		return infra.MetricsErrInternal
-	}
-	snetPeer := request.Peer.(*snet.UDPAddr)
-	labels.Src = snetPeer.IA
-	rw, ok := infra.ResponseWriterFromContext(ctx)
-	if !ok {
-		logger.Error("[syncHandler] Unable to service request, no Messenger found")
-		metrics.Sync.Registrations(labels).Inc()
-		return infra.MetricsErrInternal
-	}
-	sendAck := messenger.SendAckHelper(ctx, rw)
-	if err := segSync.ParseRaw(); err != nil {
-		logger.Error("[syncHandler] Failed to parse message", "err", err)
-		sendAck(proto.Ack_ErrCode_reject, messenger.AckRejectFailedToParse)
-		metrics.Sync.Registrations(labels.WithResult(metrics.ErrParse)).Inc()
-		return infra.MetricsErrInvalid
-	}
-	logSegRecs(logger, "[syncHandler]", request.Peer, segSync.SegRecs)
-	peerPath, err := snetPeer.GetPath()
-	if err != nil {
-		logger.Error("[syncHandler] Failed to initialize path", "err", err)
-		sendAck(proto.Ack_ErrCode_reject, messenger.AckRejectFailedToParse)
-		metrics.Sync.Registrations(labels.WithResult(metrics.ErrParse)).Inc()
-		return infra.MetricsErrInvalid
-	}
-	svcToQuery := &snet.SVCAddr{
-		IA:      snetPeer.IA,
-		Path:    peerPath.Path(),
-		NextHop: peerPath.UnderlayNextHop(),
-		SVC:     addr.SvcPS,
-	}
-
-	segs := seghandler.Segments{
-		Segs:      segSync.Recs,
-		SRevInfos: segSync.SRevInfos,
-	}
-	res := h.SegHandler.Handle(ctx, segs, svcToQuery, nil)
-	// wait until processing is done.
-	<-res.FullReplyProcessed()
-	if err := res.Err(); err != nil {
-		metrics.Sync.Registrations(labels.WithResult(metrics.ErrDB)).Inc()
-		sendAck(proto.Ack_ErrCode_reject, err.Error())
-		return infra.MetricsErrInvalid
-	}
-	if len(res.VerificationErrors()) > 0 {
-		log.FromCtx(ctx).Info("[syncHandler] Error during verification of segments/revocations",
-			"errors", res.VerificationErrors().ToError())
-	}
-	metrics.Sync.RegistrationSuccess(labels,
-		res.Stats().SegsInserted(),
-		res.Stats().SegsUpdated())
-	sendAck(proto.Ack_ErrCode_ok, "")
-	return infra.MetricsResultOk
-}
diff --git a/go/cs/metrics/requests.go b/go/cs/metrics/requests.go
index 017dc167e..38d5f5a73 100644
--- a/go/cs/metrics/requests.go
+++ b/go/cs/metrics/requests.go
@@ -108,14 +108,8 @@ func (r Request) RepliedRevs(l RequestOkLabels) prometheus.Counter {
 // DetermineReplyType determines which type of segments is in the reply. The
 // method assumes that segs only contains one type of segments.
 func DetermineReplyType(segs segfetcher.Segments) proto.PathSegType {
-	switch {
-	case len(segs.Up) > 0:
-		return proto.PathSegType_up
-	case len(segs.Core) > 0:
-		return proto.PathSegType_core
-	case len(segs.Down) > 0:
-		return proto.PathSegType_down
-	default:
-		return proto.PathSegType_unset
+	if len(segs) > 0 {
+		return segs[0].Type
 	}
+	return proto.PathSegType_unset
 }
diff --git a/go/cs/segreq/BUILD.bazel b/go/cs/segreq/BUILD.bazel
index 840508c40..2b1feeecc 100644
--- a/go/cs/segreq/BUILD.bazel
+++ b/go/cs/segreq/BUILD.bazel
@@ -3,18 +3,20 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
 go_library(
     name = "go_default_library",
     srcs = [
-        "db.go",
+        "authoritative.go",
         "doc.go",
+        "expander.go",
+        "fetcher.go",
+        "forwarder.go",
         "handler.go",
         "helpers.go",
-        "provider.go",
         "splitter.go",
-        "validator.go",
     ],
     importpath = "github.com/scionproto/scion/go/cs/segreq",
     visibility = ["//visibility:public"],
     deps = [
         "//go/cs/metrics:go_default_library",
+        "//go/cs/segutil:go_default_library",
         "//go/lib/addr:go_default_library",
         "//go/lib/common:go_default_library",
         "//go/lib/ctrl/path_mgmt:go_default_library",
@@ -27,6 +29,9 @@ go_library(
         "//go/lib/pathdb/query:go_default_library",
         "//go/lib/revcache:go_default_library",
         "//go/lib/serrors:go_default_library",
+        "//go/lib/snet:go_default_library",
+        "//go/lib/snet/addrutil:go_default_library",
+        "//go/lib/topology:go_default_library",
         "//go/pkg/trust:go_default_library",
         "//go/proto:go_default_library",
     ],
@@ -35,24 +40,17 @@ go_library(
 go_test(
     name = "go_default_test",
     srcs = [
-        "db_test.go",
+        "authoritative_test.go",
+        "forwarder_test.go",
         "helpers_test.go",
-        "provider_test.go",
-        "splitter_test.go",
     ],
     embed = [":go_default_library"],
     deps = [
         "//go/lib/addr:go_default_library",
-        "//go/lib/ctrl/path_mgmt:go_default_library",
-        "//go/lib/ctrl/seg:go_default_library",
-        "//go/lib/infra/modules/segfetcher:go_default_library",
-        "//go/lib/infra/modules/segfetcher/mock_segfetcher:go_default_library",
-        "//go/lib/pathdb/mock_pathdb:go_default_library",
-        "//go/lib/pathdb/query:go_default_library",
-        "//go/lib/revcache:go_default_library",
-        "//go/lib/revcache/mock_revcache:go_default_library",
         "//go/lib/xtest:go_default_library",
+        "//go/pkg/trust:go_default_library",
         "//go/pkg/trust/mock_trust:go_default_library",
+        "//go/proto:go_default_library",
         "@com_github_golang_mock//gomock:go_default_library",
         "@com_github_stretchr_testify//assert:go_default_library",
         "@com_github_stretchr_testify//require:go_default_library",
diff --git a/go/cs/segreq/authoritative.go b/go/cs/segreq/authoritative.go
new file mode 100644
index 000000000..f5bd79562
--- /dev/null
+++ b/go/cs/segreq/authoritative.go
@@ -0,0 +1,133 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
+	"github.com/scionproto/scion/go/lib/infra"
+	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/pathdb/query"
+	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/proto"
+)
+
+// NewAuthoritativeHandler creates a segment request handler that returns down
+// and core segments starting at this core AS.
+// This handler is used exclusively in core ASes.
+func NewAuthoritativeHandler(ia addr.IA, inspector trust.Inspector, pathDB pathdb.PathDB,
+	revCache revcache.RevCache) infra.Handler {
+
+	return &baseHandler{
+		processor: &authoritativeProcessor{
+			localIA:     ia,
+			coreChecker: CoreChecker{inspector},
+			pathDB:      pathDB,
+		},
+		revCache: revCache,
+	}
+}
+
+// authoritativeProcesser handles segment requests for down and core segents
+// starting at this core AS.
+type authoritativeProcessor struct {
+	localIA     addr.IA
+	coreChecker CoreChecker
+	pathDB      pathdb.PathDB
+}
+
+func (h *authoritativeProcessor) process(ctx context.Context,
+	req *path_mgmt.SegReq) (segfetcher.Segments, error) {
+
+	src := req.SrcIA()
+	dst := req.DstIA()
+	segType, err := h.classify(ctx, src, dst)
+	if err != nil {
+		return nil, err
+	}
+
+	switch segType {
+	case proto.PathSegType_down:
+		return getDownSegments(ctx, h.pathDB, h.localIA, dst)
+	case proto.PathSegType_core:
+		return getCoreSegments(ctx, h.pathDB, h.localIA, dst)
+	default:
+		panic("unexpected segType")
+	}
+}
+
+// classify validates the request and determines the segment type for the request
+func (h *authoritativeProcessor) classify(ctx context.Context,
+	src, dst addr.IA) (proto.PathSegType, error) {
+
+	switch {
+	case src != h.localIA:
+		return proto.PathSegType_unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+			"src", src, "dst", dst, "reason", "src must be local AS")
+	case dst.I == 0:
+		return proto.PathSegType_unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+			"src", src, "dst", dst, "reason", "zero ISD dst")
+	case dst.I == h.localIA.I:
+		dstCore, err := h.coreChecker.IsCore(ctx, dst)
+		if err != nil {
+			return proto.PathSegType_unset, err
+		}
+		if dstCore {
+			return proto.PathSegType_core, nil
+		}
+		return proto.PathSegType_down, nil
+	default:
+		// We can assume that destination in a remote ISD are core -- otherwise
+		// we should not have been asked this and we simply won't find paths.
+		return proto.PathSegType_core, nil
+	}
+}
+
+// getCoreSegments loads core segments from localIA to dstIA from the path DB.
+// Wildcard dstIA is allowed.
+func getCoreSegments(ctx context.Context, pathDB pathdb.PathDB,
+	localIA, dstIA addr.IA) (segfetcher.Segments, error) {
+
+	res, err := pathDB.Get(ctx, &query.Params{
+		StartsAt: []addr.IA{dstIA},
+		EndsAt:   []addr.IA{localIA},
+		SegTypes: []proto.PathSegType{proto.PathSegType_core},
+	})
+	if err != nil {
+		return segfetcher.Segments{}, err
+	}
+	return res.SegMetas(), nil
+}
+
+// getDownSegments loads down segments from localIA to dstIA from the path DB.
+// Wildcard dstIA is _not_ allowed.
+func getDownSegments(ctx context.Context, pathDB pathdb.PathDB,
+	localIA, dstIA addr.IA) (segfetcher.Segments, error) {
+
+	res, err := pathDB.Get(ctx, &query.Params{
+		StartsAt: []addr.IA{localIA},
+		EndsAt:   []addr.IA{dstIA},
+		SegTypes: []proto.PathSegType{proto.PathSegType_down},
+	})
+	if err != nil {
+		return segfetcher.Segments{}, err
+	}
+	return res.SegMetas(), nil
+}
diff --git a/go/cs/segreq/db.go b/go/cs/segreq/db.go
deleted file mode 100644
index bb78443fc..000000000
--- a/go/cs/segreq/db.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq
-
-import (
-	"context"
-	"time"
-
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/pathdb"
-)
-
-// PathDB is a wrapper around the path db that changes GetNextQuery behavior
-// for usage in segfetcher.
-type PathDB struct {
-	pathdb.PathDB
-	LocalInfo segfetcher.LocalInfo
-}
-
-func (db *PathDB) GetNextQuery(ctx context.Context, src, dst addr.IA,
-	policy pathdb.PolicyHash) (time.Time, error) {
-	if local, err := db.LocalInfo.IsSegLocal(ctx, src, dst); err != nil {
-		return time.Time{}, err
-	} else if local {
-		return time.Now().Add(24 * time.Hour), nil
-	}
-	return db.PathDB.GetNextQuery(ctx, src, dst, policy)
-}
-
-// CoreLocalInfo implements local info for core PSes.
-type CoreLocalInfo struct {
-	CoreChecker CoreChecker
-	LocalIA     addr.IA
-}
-
-// IsSegLocal returns whether the segments described by src and dst would be a
-// core segments or a local down segment.
-func (i *CoreLocalInfo) IsSegLocal(ctx context.Context, src, dst addr.IA) (bool, error) {
-	// All local core and down segments.
-	if dst.I == i.LocalIA.I {
-		return true, nil
-	}
-	// All core segments
-	isCore, err := i.CoreChecker.IsCore(ctx, dst)
-	if err != nil {
-		return false, err
-	}
-	return isCore, nil
-}
-
-// NonCoreLocalInfo is the local info for non core PSes.
-type NonCoreLocalInfo struct {
-	LocalIA addr.IA
-}
-
-// IsSegLocal checks if the segment described by src and dst is an up segment
-// to the local core.
-func (i *NonCoreLocalInfo) IsSegLocal(ctx context.Context, src, dst addr.IA) (bool, error) {
-	// The validator should make sure that if we are at the source it can only
-	// be an up segment.
-	return i.LocalIA.Equal(src), nil
-}
diff --git a/go/cs/segreq/expander.go b/go/cs/segreq/expander.go
new file mode 100644
index 000000000..ba3706dae
--- /dev/null
+++ b/go/cs/segreq/expander.go
@@ -0,0 +1,99 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/pathdb/query"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/proto"
+)
+
+type wildcardExpander struct {
+	localIA   addr.IA
+	core      bool
+	inspector trust.Inspector
+	pathDB    pathdb.PathDB
+}
+
+func (e *wildcardExpander) ExpandSrcWildcard(ctx context.Context,
+	req segfetcher.Request) (segfetcher.Requests, error) {
+
+	if req.Src.A != 0 {
+		return segfetcher.Requests{req}, nil
+	}
+
+	switch req.SegType {
+	case proto.PathSegType_core:
+		cores, err := e.providerCoreASes(ctx)
+		if err != nil {
+			return nil, err
+		}
+		return requestsSrcsToDst(cores, req.Dst, req.SegType), nil
+	case proto.PathSegType_down:
+		cores, err := e.coreASes(ctx, req.Src.I)
+		if err != nil {
+			return nil, err
+		}
+		return requestsSrcsToDst(cores, req.Dst, req.SegType), nil
+	default:
+		// no wildcard source for up requests
+		panic("Unexpected wildcard for up segment request, should not have passed validation")
+	}
+}
+
+// coreASes queries the core ASes in isd.
+func (e *wildcardExpander) coreASes(ctx context.Context, isd addr.ISD) ([]addr.IA, error) {
+	coreASes, err := e.inspector.ByAttributes(ctx, isd, trust.Core)
+	if err != nil {
+		return nil, serrors.WrapStr("failed to get local core ASes", err)
+	}
+	return coreASes, nil
+}
+
+// providerCoreASes returns the core ASes that are providers of this AS, i.e.
+// those core ASes that are directly reachable with an up segment
+func (e *wildcardExpander) providerCoreASes(ctx context.Context) ([]addr.IA, error) {
+
+	if e.core {
+		return []addr.IA{e.localIA}, nil
+	}
+
+	params := &query.Params{
+		SegTypes: []proto.PathSegType{proto.PathSegType_up},
+	}
+	res, err := e.pathDB.Get(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+	segs := query.Results(res).Segs()
+	return segs.FirstIAs(), nil
+}
+
+// requestsSrcsToDst creates a slice containing a request for each src in srcs to dst
+func requestsSrcsToDst(srcs []addr.IA, dst addr.IA, segType proto.PathSegType) segfetcher.Requests {
+	requests := make(segfetcher.Requests, 0, len(srcs))
+	for _, src := range srcs {
+		if src != dst {
+			requests = append(requests, segfetcher.Request{Src: src, Dst: dst, SegType: segType})
+		}
+	}
+	return requests
+}
diff --git a/go/cs/segreq/fetcher.go b/go/cs/segreq/fetcher.go
new file mode 100644
index 000000000..afe0f62a3
--- /dev/null
+++ b/go/cs/segreq/fetcher.go
@@ -0,0 +1,197 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+	"fmt"
+	"math/rand"
+	"net"
+	"time"
+
+	"github.com/scionproto/scion/go/cs/metrics"
+	"github.com/scionproto/scion/go/cs/segutil"
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/infra"
+	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/pathdb/query"
+	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/lib/snet"
+	"github.com/scionproto/scion/go/lib/snet/addrutil"
+	"github.com/scionproto/scion/go/lib/topology"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/proto"
+)
+
+type FetcherConfig struct {
+	IA           addr.IA
+	TopoProvider topology.Provider
+	Inspector    trust.Inspector
+
+	// QueryInterval specifies after how much time segments should be
+	// refetched at the remote server.
+	QueryInterval time.Duration
+	// Verifier is the verifier to use.
+	Verifier infra.Verifier
+	// PathDB is the path db to use.
+	PathDB pathdb.PathDB
+	// RevCache is the revocation cache to use.
+	RevCache revcache.RevCache
+	// RequestAPI is the request api to use.
+	RequestAPI segfetcher.RequestAPI
+
+	HeaderV2 bool
+}
+
+// NewFetcher creates a segment fetcher configured for fetching segments from
+// inside the control service
+func NewFetcher(cfg FetcherConfig) *segfetcher.Fetcher {
+
+	fetcher := segfetcher.FetcherConfig{
+		QueryInterval:    cfg.QueryInterval,
+		Verifier:         cfg.Verifier,
+		PathDB:           cfg.PathDB,
+		RevCache:         cfg.RevCache,
+		RequestAPI:       cfg.RequestAPI,
+		LocalInfo:        &localInfo{cfg.IA},
+		MetricsNamespace: metrics.PSNamespace,
+		DstProvider:      nil, // see below
+	}.New()
+	// Recursive/cyclic structure: the dstProvider in the fetcher uses the
+	// fetcher (see notes on dstProvider below).
+	fetcher.Requester.(*segfetcher.DefaultRequester).DstProvider = &dstProvider{
+		localIA: cfg.IA,
+		router:  newRouter(cfg, fetcher),
+		segSelector: &SegSelector{
+			PathDB:       cfg.PathDB,
+			RevCache:     cfg.RevCache,
+			TopoProvider: cfg.TopoProvider,
+			Pather:       addrutil.NewPather(cfg.TopoProvider, cfg.HeaderV2),
+		},
+	}
+
+	return fetcher
+}
+
+// NewRouter creates a new Router/Pather/Fetcher, configured for obtaining paths
+// from inside the control service
+func NewRouter(cfg FetcherConfig) snet.Router {
+	fetcher := NewFetcher(cfg)
+	return newRouter(cfg, fetcher)
+}
+
+func newRouter(cfg FetcherConfig, fetcher *segfetcher.Fetcher) snet.Router {
+	return &segutil.Router{
+		Pather: segfetcher.Pather{
+			TopoProvider: cfg.TopoProvider,
+			RevCache:     cfg.RevCache,
+			Splitter: NewSplitter(
+				cfg.IA,
+				cfg.TopoProvider.Get().Core(),
+				cfg.Inspector,
+				cfg.PathDB),
+			Fetcher: fetcher,
+		},
+	}
+}
+
+type localInfo struct {
+	localIA addr.IA
+}
+
+// IsSegLocal returns true for segments requests that can be answered authoritatively:
+// if this is a non-core AS:
+//  - only up segment requests
+// if this is a core AS:
+//  - down segment requests starting at this AS
+//  - core segment requests starting at this AS
+// In summary, these are exactly the segments starting at the local AS.
+func (l *localInfo) IsSegLocal(req segfetcher.Request) bool {
+	return req.Src == l.localIA
+}
+
+// dstProvider provides the address of and the path to the authoritative server
+// for a request in the segfetcher.Fetcher (or more specifically, Requester).
+// The authoritative server is the core PS at the source of the requested
+// segment.
+// Certain queries (down segment requests) must be sent to ASes for which the
+// path is not a priori locally known. Therefore, this recursively makes use of
+// the Fetcher (via Router and Pather) to obtain this path information.
+// - Core segment requests are sent only to provider core ASes, so the path
+//   will consist of only an up segment.
+// - Down segment requests are sent to all core ASes in the destination ISD.
+//   The path consists of an up segment and a core segment.
+//   The up segment is always locally available, but the core segment might
+//   have to be fetched.
+// The recursion depth, at runtime, is limited to 2, as this will _only_ be
+// called to fetch core segments when requesting down segments.
+type dstProvider struct {
+	localIA     addr.IA
+	router      snet.Router
+	segSelector *SegSelector
+}
+
+// Dsts provides the address of and the path to the authoritative server for
+// this request.
+func (p *dstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr, error) {
+
+	// The request is directed to the AS at the start of the requested segment:
+	dst := req.Src
+
+	var path snet.Path
+	switch req.SegType {
+	case proto.PathSegType_core:
+		// fast/simple path for core segment requests (only up segment required).
+		// Must NOT use the router recursively here;
+		// as it tries to find all paths, including paths through other core ASes,
+		// the router translates a path lookup to a core to the wildcard segment
+		// requests (up localIA->*) and (core *->dst). Looking up the core segment
+		// would then lead to an infinite recursion.
+		up, err := p.upPath(ctx, dst)
+		if err != nil {
+			return nil, serrors.Wrap(segfetcher.ErrNotReachable, err)
+		}
+		path = up
+	case proto.PathSegType_down:
+		// Select a random path (just like we choose a random segment above)
+		// Avoids potentially being stuck with a broken but not revoked path;
+		// allows clients to retry with possibly different path in case of failure.
+		paths, err := p.router.AllRoutes(ctx, dst)
+		if err != nil {
+			return nil, serrors.Wrap(segfetcher.ErrNotReachable, err)
+		}
+		path = paths[rand.Intn(len(paths))]
+	default:
+		panic(fmt.Errorf("Unsupported segment type for request forwarding. "+
+			"Up segment should have been resolved locally. SegType: %s", req.SegType))
+	}
+	addr := &snet.SVCAddr{
+		IA:      path.Destination(),
+		Path:    path.Path(),
+		NextHop: path.UnderlayNextHop(),
+		SVC:     addr.SvcPS,
+	}
+	return addr, nil
+}
+
+func (p *dstProvider) upPath(ctx context.Context, dst addr.IA) (snet.Path, error) {
+	return p.segSelector.SelectSeg(ctx, &query.Params{
+		StartsAt: []addr.IA{dst},
+		EndsAt:   []addr.IA{p.localIA},
+		SegTypes: []proto.PathSegType{proto.PathSegType_up},
+	})
+}
diff --git a/go/cs/segreq/forwarder.go b/go/cs/segreq/forwarder.go
new file mode 100644
index 000000000..a5869fb35
--- /dev/null
+++ b/go/cs/segreq/forwarder.go
@@ -0,0 +1,132 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
+	"github.com/scionproto/scion/go/lib/infra"
+	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/proto"
+)
+
+// NewForwardingHandler creates a forwarding segment request handler.
+// This handler is used (exclusively) for AS-local segment requests.
+func NewForwardingHandler(ia addr.IA, core bool, inspector trust.Inspector,
+	pathDB pathdb.PathDB, revCache revcache.RevCache, fetcher *segfetcher.Fetcher) infra.Handler {
+
+	return &baseHandler{
+		processor: &forwarder{
+			localIA:     ia,
+			coreChecker: CoreChecker{inspector},
+			fetcher:     fetcher,
+			expander: &wildcardExpander{
+				localIA:   ia,
+				core:      core,
+				inspector: inspector,
+				pathDB:    pathDB,
+			},
+		},
+		revCache: revCache,
+	}
+}
+
+// forwarder is the processor for segment requests for AS-local segment requests.
+// - requests for up segment are answered directly, from the local DB
+// - down and core segments are forwarded to the responsible core ASes, and results are cached
+type forwarder struct {
+	localIA     addr.IA
+	coreChecker CoreChecker
+	fetcher     *segfetcher.Fetcher
+	expander    *wildcardExpander
+}
+
+func (h *forwarder) process(ctx context.Context,
+	req *path_mgmt.SegReq) (segfetcher.Segments, error) {
+
+	src := req.SrcIA()
+	dst := req.DstIA()
+	segType, err := h.classify(ctx, src, dst)
+	if err != nil {
+		return segfetcher.Segments{}, err
+	}
+
+	reqs, err := h.expander.ExpandSrcWildcard(ctx,
+		segfetcher.Request{Src: src, Dst: dst, SegType: segType})
+	if err != nil {
+		return segfetcher.Segments{},
+			serrors.WrapStr("failed to expand core wildcard request", err)
+	}
+
+	return h.fetcher.Fetch(ctx, reqs, false)
+}
+
+// classify validates the request and determines the segment type for the request
+func (h *forwarder) classify(ctx context.Context, src, dst addr.IA) (proto.PathSegType, error) {
+	unset := proto.PathSegType_unset // shorthand
+	if src.I == 0 || dst.I == 0 {
+		return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+			"src", src, "dst", dst, "reason", "zero ISD src or dst")
+	}
+	if dst == h.localIA {
+		// this could be an otherwise valid request, but probably the requester switched Src and Dst
+		return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+			"src", src, "dst", dst, "reason", "dst is local AS, confusion?")
+	}
+	srcCore, err := h.coreChecker.IsCore(ctx, src)
+	if err != nil {
+		return proto.PathSegType_unset, err
+	}
+	dstCore, err := h.coreChecker.IsCore(ctx, dst)
+	if err != nil {
+		return proto.PathSegType_unset, err
+	}
+	switch {
+	case srcCore && dstCore:
+		// core
+		if src.I != h.localIA.I {
+			return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+				"src", src, "dst", dst, "reason", "core segment request src ISD not local ISD")
+		}
+		return proto.PathSegType_core, nil
+	case srcCore:
+		// down
+		if src.I != dst.I {
+			return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+				"src", src, "dst", dst, "reason", "down segment request src/dst in different ISD")
+		}
+		return proto.PathSegType_down, nil
+	case dstCore:
+		// up
+		if src != h.localIA {
+			return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+				"src", src, "dst", dst, "reason", "up segment request src not local AS")
+		}
+		if dst.I != h.localIA.I {
+			return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+				"src", src, "dst", dst, "reason", "up segment request dst in different ISD")
+		}
+		return proto.PathSegType_up, nil
+	default:
+		return unset, serrors.WithCtx(segfetcher.ErrInvalidRequest,
+			"src", src, "dst", dst, "reason", "non-core src & dst")
+	}
+}
diff --git a/go/cs/segreq/handler.go b/go/cs/segreq/handler.go
index 43ba42685..551dc639e 100644
--- a/go/cs/segreq/handler.go
+++ b/go/cs/segreq/handler.go
@@ -15,28 +15,37 @@
 package segreq
 
 import (
+	"context"
+
 	"github.com/scionproto/scion/go/cs/metrics"
-	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/revcache"
-	"github.com/scionproto/scion/go/pkg/trust"
 	"github.com/scionproto/scion/go/proto"
 )
 
-// Handler handles segment requests.
-type Handler struct {
-	Fetcher  *segfetcher.Fetcher
-	RevCache revcache.RevCache
+// baseHandler is an abstract handler for SegReq containing the common
+// boilerplate for the different, concrete processors.
+type baseHandler struct {
+	processor processor
+	revCache  revcache.RevCache
+}
+
+// processor is the interface for a SegReq processor. It defines the interfacs
+// of the core logic of a SegReq handler. This is called by the boilerplate
+// baseHandler.
+type processor interface {
+	process(context.Context, *path_mgmt.SegReq) (segfetcher.Segments, error)
 }
 
-// Handle handles a segment request.
-func (h *Handler) Handle(request *infra.Request) *infra.HandlerResult {
+// Handle handles SegReq messages and deals with all the common boilerplate of setting up loggers
+// and metrics, filtering revoked segments and sending the segment reply. It calls the "processor"
+// that actually finds the segments to return.
+func (h *baseHandler) Handle(request *infra.Request) *infra.HandlerResult {
 	ctx := request.Context()
 	logger := log.FromCtx(ctx)
 	labels := metrics.RequestLabels{
@@ -60,8 +69,7 @@ func (h *Handler) Handle(request *infra.Request) *infra.HandlerResult {
 	}
 	sendAck := messenger.SendAckHelper(ctx, rw)
 
-	segs, err := h.Fetcher.FetchSegs(ctx,
-		segfetcher.Request{Src: segReq.SrcIA(), Dst: segReq.DstIA()})
+	segs, err := h.processor.process(ctx, segReq)
 	if err != nil {
 		// TODO(lukedirtwalker): Define clearer the different errors that can
 		// occur and depending on them reply / return different error codes.
@@ -71,7 +79,7 @@ func (h *Handler) Handle(request *infra.Request) *infra.HandlerResult {
 		return infra.MetricsErrInternal
 	}
 	labels.SegType = metrics.DetermineReplyType(segs)
-	revs, err := revcache.RelevantRevInfos(ctx, h.RevCache, segs.Up, segs.Core, segs.Down)
+	revs, err := revcache.RelevantRevInfos(ctx, h.revCache, segs.Segs())
 	if err != nil {
 		logger.Info("[segReqHandler] Failed to find relevant revocations for reply", "err", err)
 		// the client might still be able to use the segments so continue here.
@@ -79,7 +87,7 @@ func (h *Handler) Handle(request *infra.Request) *infra.HandlerResult {
 	reply := &path_mgmt.SegReply{
 		Req: segReq,
 		Recs: &path_mgmt.SegRecs{
-			Recs:      segsToRecs(ctx, segs),
+			Recs:      segs,
 			SRevInfos: revs,
 		},
 	}
@@ -95,55 +103,3 @@ func (h *Handler) Handle(request *infra.Request) *infra.HandlerResult {
 	metrics.Requests.RepliedRevs(labels.RequestOkLabels).Add(float64(len(reply.Recs.SRevInfos)))
 	return infra.MetricsResultOk
 }
-
-// CreateLocalInfo creates the local info oracle.
-func CreateLocalInfo(core bool, ia addr.IA, inspector trust.Inspector) segfetcher.LocalInfo {
-	if core {
-		return &CoreLocalInfo{
-			CoreChecker: CoreChecker{Inspector: inspector},
-			LocalIA:     ia,
-		}
-	}
-	return &NonCoreLocalInfo{
-		LocalIA: ia,
-	}
-}
-
-func createPathDB(db pathdb.PathDB, localInfo segfetcher.LocalInfo) pathdb.PathDB {
-	return &PathDB{
-		PathDB:    db,
-		LocalInfo: localInfo,
-	}
-}
-
-// CreateNonCoreDstProvider creates a destination provider for a non-core AS.
-func CreateNonCoreDstProvider(ia addr.IA, pather Pather, pathDB pathdb.PathDB,
-	revCache revcache.RevCache, inspector trust.Inspector) segfetcher.DstProvider {
-
-	return &nonCoreDstProvider{
-		SegSelector: SegSelector{
-			PathDB:   pathDB,
-			RevCache: revCache,
-		},
-		inspector:   inspector,
-		coreChecker: CoreChecker{Inspector: inspector},
-		localIA:     ia,
-		pathDB:      pathDB,
-		pather:      pather,
-	}
-}
-
-// CreateCoreDstProvider creates destination provider for a core AS.
-func CreateCoreDstProvider(ia addr.IA, pather Pather, pathDB pathdb.PathDB,
-	revCache revcache.RevCache) segfetcher.DstProvider {
-
-	return &coreDstProvider{
-		SegSelector: SegSelector{
-			PathDB:   pathDB,
-			RevCache: revCache,
-		},
-		localIA: ia,
-		pathDB:  pathDB,
-		pather:  pather,
-	}
-}
diff --git a/go/cs/segreq/helpers.go b/go/cs/segreq/helpers.go
index 5f40f45fc..906713441 100644
--- a/go/cs/segreq/helpers.go
+++ b/go/cs/segreq/helpers.go
@@ -16,14 +16,18 @@ package segreq
 
 import (
 	"context"
-	"fmt"
+	"math/rand"
 
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/ctrl/seg"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/log"
+	"github.com/scionproto/scion/go/lib/pathdb"
+	"github.com/scionproto/scion/go/lib/pathdb/query"
+	"github.com/scionproto/scion/go/lib/revcache"
+	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/lib/snet"
+	"github.com/scionproto/scion/go/lib/snet/addrutil"
+	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/pkg/trust"
-	"github.com/scionproto/scion/go/proto"
 )
 
 // CoreChecker checks whether a given ia is core.
@@ -31,6 +35,7 @@ type CoreChecker struct {
 	Inspector trust.Inspector
 }
 
+// IsCore checks whether ia is a wildcard or core
 func (c *CoreChecker) IsCore(ctx context.Context, ia addr.IA) (bool, error) {
 	if ia.IsWildcard() {
 		return true, nil
@@ -38,23 +43,38 @@ func (c *CoreChecker) IsCore(ctx context.Context, ia addr.IA) (bool, error) {
 	return c.Inspector.HasAttributes(ctx, ia, trust.Core)
 }
 
-func segsToRecs(ctx context.Context, segs segfetcher.Segments) []*seg.Meta {
-	logger := log.FromCtx(ctx)
-	recs := make([]*seg.Meta, 0, len(segs.Up)+len(segs.Core)+len(segs.Down))
-	for _, s := range segs.Up {
-		logger.Debug(fmt.Sprintf("[segReqHandler:collectSegs] up %v -> %v",
-			s.FirstIA(), s.LastIA()), "seg", s.GetLoggingID())
-		recs = append(recs, seg.NewMeta(s, proto.PathSegType_up))
+// SegSelector selects segments to use for a connection to a remote server.
+type SegSelector struct {
+	PathDB       pathdb.PathDB
+	RevCache     revcache.RevCache
+	TopoProvider topology.Provider
+	Pather       addrutil.Pather
+}
+
+// SelectSeg selects a suitable segment for the given path db query.
+func (s *SegSelector) SelectSeg(ctx context.Context,
+	params *query.Params) (snet.Path, error) {
+
+	res, err := s.PathDB.Get(ctx, params)
+	if err != nil {
+		return nil, err
 	}
-	for _, s := range segs.Core {
-		logger.Debug(fmt.Sprintf("[segReqHandler:collectSegs] core %v -> %v",
-			s.FirstIA(), s.LastIA()), "seg", s.GetLoggingID())
-		recs = append(recs, seg.NewMeta(s, proto.PathSegType_core))
+	segs := query.Results(res).Segs()
+	_, err = segs.FilterSegsErr(func(ps *seg.PathSegment) (bool, error) {
+		return revcache.NoRevokedHopIntf(ctx, s.RevCache, ps)
+	})
+	if err != nil {
+		return nil, serrors.WrapStr("failed to filter segments", err)
 	}
-	for _, s := range segs.Down {
-		logger.Debug(fmt.Sprintf("[segReqHandler:collectSegs] down %v -> %v",
-			s.FirstIA(), s.LastIA()), "seg", s.GetLoggingID())
-		recs = append(recs, seg.NewMeta(s, proto.PathSegType_down))
+	if len(segs) < 1 {
+		return nil, serrors.New("no segments found")
+	}
+	seg := segs[rand.Intn(len(segs))]
+
+	svcaddr, err := s.Pather.GetPath(addr.SvcPS, seg)
+	// odd interface, builds address not path. Use GetPath to convert to snet.Path
+	if err != nil {
+		return nil, err
 	}
-	return recs
+	return svcaddr.GetPath()
 }
diff --git a/go/cs/segreq/provider.go b/go/cs/segreq/provider.go
deleted file mode 100644
index e133f7cf7..000000000
--- a/go/cs/segreq/provider.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq
-
-import (
-	"context"
-	"errors"
-	"math/rand"
-	"net"
-
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/seg"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/pathdb"
-	"github.com/scionproto/scion/go/lib/pathdb/query"
-	"github.com/scionproto/scion/go/lib/revcache"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/pkg/trust"
-	"github.com/scionproto/scion/go/proto"
-)
-
-type Pather interface {
-	GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error)
-}
-
-// SegSelector selects segments to use for a connection to a remote server.
-type SegSelector struct {
-	PathDB   pathdb.PathDB
-	RevCache revcache.RevCache
-}
-
-// SelectSeg selects a suitable segment for the given path db query.
-func (s *SegSelector) SelectSeg(ctx context.Context,
-	params *query.Params) (*seg.PathSegment, error) {
-
-	res, err := s.PathDB.Get(ctx, params)
-	if err != nil {
-		return nil, err
-	}
-	segs := query.Results(res).Segs()
-	_, err = segs.FilterSegsErr(func(ps *seg.PathSegment) (bool, error) {
-		return revcache.NoRevokedHopIntf(ctx, s.RevCache, ps)
-	})
-	if err != nil {
-		return nil, common.NewBasicError("failed to filter segments", err)
-	}
-	if len(segs) < 1 {
-		return nil, serrors.New("no segments found")
-	}
-	return segs[rand.Intn(len(segs))], nil
-}
-
-type nonCoreDstProvider struct {
-	SegSelector
-	inspector   trust.Inspector
-	coreChecker CoreChecker
-	localIA     addr.IA
-	pathDB      pathdb.PathDB
-	pather      Pather
-}
-
-// Dst provides the server to lookup the segment for the given request.
-// Querying for segments that start at the localIA will result in an error,
-// since they should be locally resolved.
-func (p *nonCoreDstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr, error) {
-	if p.localIA.Equal(req.Src) {
-		return nil, serrors.WithCtx(segfetcher.ErrInvalidRequest,
-			"req", req, "reason", "up segments should be resolved locally")
-	}
-	dstCore, err := p.coreChecker.IsCore(ctx, req.Dst)
-	if err != nil {
-		return nil, err
-	}
-	if dstCore {
-		// for a core segment request we have to request the segments at the
-		// given start point (core PS).
-		return p.coreSvcAddr(ctx, addr.SvcPS, []addr.IA{req.Src})
-	}
-	// for all other requests, i.e. down requests we can ask any core.
-	cores, err := p.inspector.ByAttributes(ctx, p.localIA.I, trust.Core)
-	if err != nil {
-		return nil, err
-	}
-	return p.coreSvcAddr(ctx, addr.SvcPS, cores)
-}
-
-func (p *nonCoreDstProvider) coreSvcAddr(ctx context.Context, svc addr.HostSVC,
-	coreASes []addr.IA) (net.Addr, error) {
-
-	params := &query.Params{
-		SegTypes: []proto.PathSegType{proto.PathSegType_up},
-		StartsAt: coreASes,
-		EndsAt:   []addr.IA{p.localIA},
-	}
-	seg, err := p.SelectSeg(ctx, params)
-	if err != nil {
-		return nil, serrors.Wrap(segfetcher.ErrNotReachable, err)
-	}
-	return p.pather.GetPath(svc, seg)
-}
-
-type coreDstProvider struct {
-	SegSelector
-	localIA addr.IA
-	pathDB  pathdb.PathDB
-	pather  Pather
-}
-
-func (p *coreDstProvider) Dst(ctx context.Context, req segfetcher.Request) (net.Addr, error) {
-	dstISDLocal := req.Dst.I == p.localIA.I
-	if dstISDLocal {
-		return nil, errors.New("shouldn't need resolution for local dst")
-	}
-	return p.corePSAddr(ctx, req.Dst.I)
-}
-
-func (p *coreDstProvider) corePSAddr(ctx context.Context, destISD addr.ISD) (net.Addr, error) {
-	params := &query.Params{
-		SegTypes: []proto.PathSegType{proto.PathSegType_core},
-		StartsAt: []addr.IA{{I: destISD}},
-		EndsAt:   []addr.IA{p.localIA},
-	}
-	seg, err := p.SelectSeg(ctx, params)
-	if err != nil {
-		return nil, serrors.Wrap(segfetcher.ErrNotReachable, err)
-	}
-	return p.pather.GetPath(addr.SvcPS, seg)
-}
diff --git a/go/cs/segreq/splitter.go b/go/cs/segreq/splitter.go
index bafd6fb48..4c8a13b09 100644
--- a/go/cs/segreq/splitter.go
+++ b/go/cs/segreq/splitter.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Anapaya Systems
+// Copyright 2020 ETH Zurich
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -19,48 +19,56 @@ import (
 
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/serrors"
+	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/pkg/trust"
 )
 
-// Splitter splits requests for the PS.
-type Splitter struct {
-	ASInspector trust.Inspector
+// NewSplitter creates a segfetcher.Splitter for a segfetcher.Pather used in the path service.
+func NewSplitter(ia addr.IA, core bool, inspector trust.Inspector,
+	pathDB pathdb.PathDB) segfetcher.Splitter {
+
+	return &splitter{
+		Splitter: &segfetcher.MultiSegmentSplitter{
+			LocalIA:   ia,
+			Core:      core,
+			Inspector: inspector,
+		},
+		expander: &wildcardExpander{
+			localIA:   ia,
+			core:      core,
+			inspector: inspector,
+			pathDB:    pathDB,
+		},
+	}
 }
 
-func (s *Splitter) Split(ctx context.Context,
-	r segfetcher.Request) (segfetcher.RequestSet, error) {
+// splitter is a segfetcher.Splitter for a segfetcher.Pather used in the path service.
+// The default splittler returns a list of (up to three) wildcard segment requests.
+// These wildcards are normally resolved by segreq.forwarder, the AS-local
+// segment request handler.
+// However, segment requests _by_ the control server itself don't pass through
+// this forwarding handler, so this custom splitter is used to expand wildcard
+// requests.
+type splitter struct {
+	segfetcher.Splitter
+	expander *wildcardExpander
+}
 
-	srcCore, err := s.isCore(ctx, r.Src)
-	if err != nil {
-		return segfetcher.RequestSet{}, err
-	}
-	dstCore, err := s.isCore(ctx, r.Dst)
+// Split splits a path request from the local AS to dst into a set of segment
+// requests.
+func (s *splitter) Split(ctx context.Context, dst addr.IA) (segfetcher.Requests, error) {
+	wcReqs, err := s.Splitter.Split(ctx, dst)
 	if err != nil {
-		return segfetcher.RequestSet{}, err
+		return nil, err
 	}
-	switch {
-	case !srcCore && dstCore:
-		return segfetcher.RequestSet{Up: r}, nil
-	case srcCore && dstCore:
-		return segfetcher.RequestSet{Cores: segfetcher.Requests{r}}, nil
-	case srcCore && !dstCore:
-		return segfetcher.RequestSet{Down: r}, nil
-	default:
-		return segfetcher.RequestSet{}, segfetcher.ErrInvalidRequest
-	}
-}
 
-func (s *Splitter) isCore(ctx context.Context, ia addr.IA) (bool, error) {
-	if ia.IsZero() {
-		return false, serrors.WithCtx(segfetcher.ErrInvalidRequest, "reason", "empty ia")
-	}
-	if ia.IsWildcard() {
-		return true, nil
-	}
-	isCore, err := s.ASInspector.HasAttributes(ctx, ia, trust.Core)
-	if err != nil {
-		return false, err
+	var reqs segfetcher.Requests
+	for _, req := range wcReqs {
+		expanded, err := s.expander.ExpandSrcWildcard(ctx, req)
+		if err != nil {
+			return nil, err
+		}
+		reqs = append(reqs, expanded...)
 	}
-	return isCore, nil
+	return reqs, nil
 }
diff --git a/go/cs/segreq/validator.go b/go/cs/segreq/validator.go
deleted file mode 100644
index 97011f77f..000000000
--- a/go/cs/segreq/validator.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq
-
-import (
-	"context"
-
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/serrors"
-)
-
-// BaseValidator is the base validation for all PSes.
-type BaseValidator struct {
-	CoreChecker
-}
-
-// Validate validates the request.
-func (v *BaseValidator) Validate(ctx context.Context, r segfetcher.Request) error {
-	wildSrc := r.Src.IsWildcard()
-	wildDst := r.Dst.IsWildcard()
-	sameIsd := r.Src.I == r.Dst.I
-	coreSrc, err := v.IsCore(ctx, r.Src)
-	if err != nil {
-		return err
-	}
-	coreDst, err := v.IsCore(ctx, r.Dst)
-	if err != nil {
-		return err
-	}
-	switch {
-	case r.IsZero() || r.Src.IsZero() || r.Dst.IsZero():
-		return serrors.WithCtx(segfetcher.ErrInvalidRequest,
-			"req", r, "reason", "zero src or dst")
-	case wildSrc && wildDst:
-		return serrors.WithCtx(segfetcher.ErrInvalidRequest,
-			"req", r, "reason", "wildcard src & dst")
-	case !coreSrc && (coreDst || wildDst) && sameIsd:
-		// up segment
-		return nil
-	case coreSrc && coreDst:
-		// core segment
-		return nil
-	case (coreSrc || wildSrc) && !coreDst && sameIsd:
-		// down segment
-		return nil
-	default:
-		return serrors.WithCtx(segfetcher.ErrInvalidRequest,
-			"req", r, "reason", "not a single segment")
-	}
-}
-
-// CoreValidator is the validator for core PSes.
-type CoreValidator struct {
-	BaseValidator
-}
-
-// Validate validates the request.
-func (v *CoreValidator) Validate(ctx context.Context, r segfetcher.Request) error {
-	if err := v.BaseValidator.Validate(ctx, r); err != nil {
-		return err
-	}
-	coreSrc, err := v.IsCore(ctx, r.Src)
-	if err != nil {
-		return err
-	}
-	if !coreSrc {
-		return serrors.WithCtx(segfetcher.ErrInvalidRequest,
-			"req", r, "reason", "src should be core")
-	}
-	return nil
-}
diff --git a/go/cs/segsyncer/BUILD.bazel b/go/cs/segsyncer/BUILD.bazel
deleted file mode 100644
index 2c8fe0985..000000000
--- a/go/cs/segsyncer/BUILD.bazel
+++ /dev/null
@@ -1,25 +0,0 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
-
-go_library(
-    name = "go_default_library",
-    srcs = ["segsyncer.go"],
-    importpath = "github.com/scionproto/scion/go/cs/segsyncer",
-    visibility = ["//visibility:public"],
-    deps = [
-        "//go/cs/metrics:go_default_library",
-        "//go/lib/addr:go_default_library",
-        "//go/lib/common:go_default_library",
-        "//go/lib/ctrl/path_mgmt:go_default_library",
-        "//go/lib/ctrl/seg:go_default_library",
-        "//go/lib/infra:go_default_library",
-        "//go/lib/infra/messenger:go_default_library",
-        "//go/lib/log:go_default_library",
-        "//go/lib/pathdb:go_default_library",
-        "//go/lib/pathdb/query:go_default_library",
-        "//go/lib/periodic:go_default_library",
-        "//go/lib/revcache:go_default_library",
-        "//go/lib/serrors:go_default_library",
-        "//go/pkg/trust:go_default_library",
-        "//go/proto:go_default_library",
-    ],
-)
diff --git a/go/cs/segsyncer/segsyncer.go b/go/cs/segsyncer/segsyncer.go
deleted file mode 100644
index abe74d1e0..000000000
--- a/go/cs/segsyncer/segsyncer.go
+++ /dev/null
@@ -1,260 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segsyncer
-
-import (
-	"context"
-	"errors"
-	"fmt"
-	"net"
-	"sort"
-	"time"
-
-	"github.com/scionproto/scion/go/cs/metrics"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
-	"github.com/scionproto/scion/go/lib/ctrl/seg"
-	"github.com/scionproto/scion/go/lib/infra"
-	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/pathdb"
-	"github.com/scionproto/scion/go/lib/pathdb/query"
-	"github.com/scionproto/scion/go/lib/periodic"
-	"github.com/scionproto/scion/go/lib/revcache"
-	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/pkg/trust"
-	"github.com/scionproto/scion/go/proto"
-)
-
-var _ periodic.Task = (*SegSyncer)(nil)
-
-var (
-	errPathDB   = serrors.New("pathdb error")
-	errRevcache = serrors.New("revcache error")
-	errNoPaths  = serrors.New("no paths")
-	errNet      = serrors.New("network error")
-)
-
-type Pather interface {
-	GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error)
-}
-
-type Config struct {
-	IA        addr.IA
-	Inspector trust.Inspector
-	Pather    Pather
-	Msgr      infra.Messenger
-	PathDB    pathdb.PathDB
-	RevCache  revcache.RevCache
-}
-
-type SegSyncer struct {
-	latestUpdate *time.Time
-	pathDB       pathdb.PathDB
-	revCache     revcache.RevCache
-	msgr         infra.Messenger
-	dstIA        addr.IA
-	localIA      addr.IA
-	pather       Pather
-	repErrCnt    int
-}
-
-func StartAll(cfg Config) ([]*periodic.Runner, error) {
-	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
-	defer cancelF()
-	coreASes, err := cfg.Inspector.ByAttributes(ctx, cfg.IA.I, trust.Core)
-	if err != nil {
-		return nil, common.NewBasicError("Failed to get local core ASes", err)
-	}
-
-	segSyncers := make([]*periodic.Runner, 0, len(coreASes)-1)
-	for _, coreAS := range coreASes {
-		if coreAS.Equal(cfg.IA) {
-			continue
-		}
-		syncer := &SegSyncer{
-			pathDB:   cfg.PathDB,
-			revCache: cfg.RevCache,
-			msgr:     cfg.Msgr,
-			dstIA:    coreAS,
-			localIA:  cfg.IA,
-			pather:   cfg.Pather,
-		}
-		// TODO(lukedirtwalker): either log or add metric to indicate
-		// if task takes longer than ticker often.
-		segSyncers = append(segSyncers,
-			periodic.Start(syncer, time.Second, 3*time.Second))
-	}
-	return segSyncers, nil
-}
-
-func (s *SegSyncer) Name() string {
-	return fmt.Sprintf("ps_segsyncer_segSyncer_%s", s.dstIA)
-}
-
-func (s *SegSyncer) Run(ctx context.Context) {
-	// TODO(lukedirtwalker): handle too many errors in s.repErrCnt.
-	labels := metrics.SyncPushLabels{
-		Result: metrics.ErrInternal,
-		Dst:    s.dstIA,
-	}
-	logger := log.FromCtx(ctx)
-	cPs, err := s.getDstAddr(ctx)
-	if err != nil {
-		logger.Error("[segsyncer.SegSyncer] Failed to find path to remote",
-			"dstIA", s.dstIA, "err", err)
-		s.repErrCnt++
-		metrics.Sync.Pushes(labels.WithResult(errToMetricsLabel(err))).Inc()
-		return
-	}
-	cnt, err := s.runInternal(ctx, cPs)
-	if err != nil {
-		logger.Error("[segsyncer.SegSyncer] Failed to send segSync",
-			"dstIA", s.dstIA, "err", err)
-		s.repErrCnt++
-		metrics.Sync.Pushes(labels.WithResult(errToMetricsLabel(err))).Inc()
-		return
-	}
-	if cnt > 0 {
-		logger.Debug("[segsyncer.SegSyncer] Sent down segments",
-			"dstIA", s.dstIA, "cnt", cnt)
-	}
-	metrics.Sync.Pushes(labels.WithResult(metrics.OkSuccess)).Inc()
-	s.repErrCnt = 0
-}
-
-func (s *SegSyncer) getDstAddr(ctx context.Context) (net.Addr, error) {
-	coreSegs, err := s.fetchCoreSegsFromDB(ctx)
-	if err != nil {
-		return nil, serrors.WrapStr("failed to get core segs", err)
-	}
-	if len(coreSegs) < 1 {
-		return nil, serrors.Wrap(errNoPaths, serrors.New("No core segments found!"))
-	}
-	var cPs net.Addr
-	// select a seg to reach the dst
-	for _, ps := range coreSegs {
-		cPs, err = s.pather.GetPath(addr.SvcPS, ps)
-		if err == nil {
-			return cPs, nil
-		}
-	}
-	return nil, err
-}
-
-func (s *SegSyncer) fetchCoreSegsFromDB(ctx context.Context) ([]*seg.PathSegment, error) {
-	params := &query.Params{
-		SegTypes: []proto.PathSegType{proto.PathSegType_core},
-		StartsAt: []addr.IA{s.dstIA},
-		EndsAt:   []addr.IA{s.localIA},
-	}
-	res, err := s.pathDB.Get(ctx, params)
-	if err != nil {
-		return nil, serrors.Wrap(errPathDB, err)
-	}
-	segs := query.Results(res).Segs()
-	_, err = segs.FilterSegsErr(func(ps *seg.PathSegment) (bool, error) {
-		return revcache.NoRevokedHopIntf(ctx, s.revCache, ps)
-	})
-	if err != nil {
-		return nil, serrors.WrapStr("failed to filter segments",
-			serrors.Wrap(errRevcache, err))
-	}
-	// Sort by number of hops, i.e. AS entries.
-	sort.Slice(segs, func(i, j int) bool {
-		return len(segs[i].ASEntries) < len(segs[j].ASEntries)
-	})
-	return segs, nil
-}
-
-func (s *SegSyncer) runInternal(ctx context.Context, cPs net.Addr) (int, error) {
-	q := &query.Params{
-		SegTypes:      []proto.PathSegType{proto.PathSegType_down},
-		StartsAt:      []addr.IA{s.localIA},
-		MinLastUpdate: s.latestUpdate,
-	}
-	queryResult, err := s.pathDB.Get(ctx, q)
-	if err != nil {
-		return 0, serrors.Wrap(errPathDB, err)
-	}
-	if len(queryResult) == 0 {
-		return 0, nil
-	}
-	msgs, err := s.createMessages(ctx, queryResult)
-	if err != nil {
-		return 0, err
-	}
-	sent := 0
-	for _, msgT := range msgs {
-		err := s.msgr.SendSegSync(ctx, msgT.msg, cPs, messenger.NextId())
-		if err != nil {
-			return sent, serrors.Wrap(errNet, err)
-		}
-		s.latestUpdate = &msgT.latestUpdate
-		sent += len(msgT.msg.SegRecs.Recs)
-	}
-	return sent, nil
-}
-
-// FIXME(lukedirtwalker): Sending a message per segment is quite a big overhead.
-// Depending on the underlying transport we could send all segments in a single message.
-// We should detect the transport and then split messages depending on the transport layer.
-func (s *SegSyncer) createMessages(ctx context.Context,
-	qrs []*query.Result) ([]*msgWithTimestamp, error) {
-
-	msgs := make([]*msgWithTimestamp, 0, len(qrs))
-	for _, qr := range qrs {
-		revs, err := revcache.RelevantRevInfos(ctx, s.revCache, []*seg.PathSegment{qr.Seg})
-		if err != nil {
-			return nil, serrors.Wrap(errRevcache, err)
-		}
-		msg := &path_mgmt.SegSync{
-			SegRecs: &path_mgmt.SegRecs{
-				Recs:      []*seg.Meta{seg.NewMeta(qr.Seg, proto.PathSegType_down)},
-				SRevInfos: revs,
-			},
-		}
-		msgs = append(msgs, &msgWithTimestamp{
-			msg:          msg,
-			latestUpdate: qr.LastUpdate,
-		})
-	}
-	return msgs, nil
-}
-
-func errToMetricsLabel(err error) string {
-	switch {
-	case serrors.IsTimeout(err):
-		return metrics.ErrTimeout
-	case errors.Is(err, errPathDB):
-		return metrics.ErrDB
-	case errors.Is(err, errRevcache):
-		return metrics.ErrDB
-	case errors.Is(err, errNoPaths):
-		return metrics.ErrNoPath
-	case errors.Is(err, errNet):
-		return metrics.ErrNetwork
-	default:
-		return metrics.ErrNotClassified
-	}
-}
-
-// msgWithTimestamp is a SegSync message
-// with the latest lastUpdate timestamp of the segments in the message.
-type msgWithTimestamp struct {
-	msg          *path_mgmt.SegSync
-	latestUpdate time.Time
-}
diff --git a/go/lib/ctrl/path_mgmt/BUILD.bazel b/go/lib/ctrl/path_mgmt/BUILD.bazel
index ef1d5a58d..818585008 100644
--- a/go/lib/ctrl/path_mgmt/BUILD.bazel
+++ b/go/lib/ctrl/path_mgmt/BUILD.bazel
@@ -11,7 +11,6 @@ go_library(
         "ifstate_req.go",
         "path_mgmt.go",
         "rev_info.go",
-        "seg_changes.go",
         "seg_recs.go",
         "seg_reply.go",
         "seg_req.go",
diff --git a/go/lib/ctrl/path_mgmt/path_mgmt.go b/go/lib/ctrl/path_mgmt/path_mgmt.go
index 44b5a86ac..615373e36 100644
--- a/go/lib/ctrl/path_mgmt/path_mgmt.go
+++ b/go/lib/ctrl/path_mgmt/path_mgmt.go
@@ -25,23 +25,18 @@ import (
 
 // union represents the contents of the unnamed capnp union.
 type union struct {
-	Which             proto.PathMgmt_Which
-	SegReq            *SegReq
-	SegReply          *SegReply
-	SegReg            *SegReg
-	SegSync           *SegSync
-	SRevInfo          *SignedRevInfo
-	IFStateReq        *IFStateReq   `capnp:"ifStateReq"`
-	IFStateInfos      *IFStateInfos `capnp:"ifStateInfos"`
-	SegChangesIdReq   *SegChangesIdReq
-	SegChangesIdReply *SegChangesIdReply
-	SegChangesReq     *SegChangesReq
-	SegChangesReply   *SegChangesReply
-	HPSegReq          *HPSegReq   `capnp:"hpSegReq"`
-	HPSegReply        *HPSegReply `capnp:"hpSegReply"`
-	HPSegReg          *HPSegReg   `capnp:"hpSegReg"`
-	HPCfgReq          *HPCfgReq   `capnp:"hpCfgReq"`
-	HPCfgReply        *HPCfgReply `capnp:"hpCfgReply"`
+	Which        proto.PathMgmt_Which
+	SegReq       *SegReq
+	SegReply     *SegReply
+	SegReg       *SegReg
+	SRevInfo     *SignedRevInfo
+	IFStateReq   *IFStateReq   `capnp:"ifStateReq"`
+	IFStateInfos *IFStateInfos `capnp:"ifStateInfos"`
+	HPSegReq     *HPSegReq     `capnp:"hpSegReq"`
+	HPSegReply   *HPSegReply   `capnp:"hpSegReply"`
+	HPSegReg     *HPSegReg     `capnp:"hpSegReg"`
+	HPCfgReq     *HPCfgReq     `capnp:"hpCfgReq"`
+	HPCfgReply   *HPCfgReply   `capnp:"hpCfgReply"`
 }
 
 func (u *union) set(c proto.Cerealizable) error {
@@ -55,9 +50,6 @@ func (u *union) set(c proto.Cerealizable) error {
 	case *SegReg:
 		u.Which = proto.PathMgmt_Which_segReg
 		u.SegReg = p
-	case *SegSync:
-		u.Which = proto.PathMgmt_Which_segSync
-		u.SegSync = p
 	case *SignedRevInfo:
 		u.Which = proto.PathMgmt_Which_sRevInfo
 		u.SRevInfo = p
@@ -67,18 +59,6 @@ func (u *union) set(c proto.Cerealizable) error {
 	case *IFStateInfos:
 		u.Which = proto.PathMgmt_Which_ifStateInfos
 		u.IFStateInfos = p
-	case *SegChangesIdReq:
-		u.Which = proto.PathMgmt_Which_segChangesIdReq
-		u.SegChangesIdReq = p
-	case *SegChangesIdReply:
-		u.Which = proto.PathMgmt_Which_segChangesIdReply
-		u.SegChangesIdReply = p
-	case *SegChangesReq:
-		u.Which = proto.PathMgmt_Which_segChangesReq
-		u.SegChangesReq = p
-	case *SegChangesReply:
-		u.Which = proto.PathMgmt_Which_segChangesReply
-		u.SegChangesReply = p
 	case *HPSegReq:
 		u.Which = proto.PathMgmt_Which_hpSegReq
 		u.HPSegReq = p
@@ -109,22 +89,12 @@ func (u *union) get() (proto.Cerealizable, error) {
 		return u.SegReply, nil
 	case proto.PathMgmt_Which_segReg:
 		return u.SegReg, nil
-	case proto.PathMgmt_Which_segSync:
-		return u.SegSync, nil
 	case proto.PathMgmt_Which_sRevInfo:
 		return u.SRevInfo, nil
 	case proto.PathMgmt_Which_ifStateReq:
 		return u.IFStateReq, nil
 	case proto.PathMgmt_Which_ifStateInfos:
 		return u.IFStateInfos, nil
-	case proto.PathMgmt_Which_segChangesIdReq:
-		return u.SegChangesIdReq, nil
-	case proto.PathMgmt_Which_segChangesIdReply:
-		return u.SegChangesIdReply, nil
-	case proto.PathMgmt_Which_segChangesReq:
-		return u.SegChangesReq, nil
-	case proto.PathMgmt_Which_segChangesReply:
-		return u.SegChangesReply, nil
 	case proto.PathMgmt_Which_hpSegReq:
 		return u.HPSegReq, nil
 	case proto.PathMgmt_Which_hpSegReply:
diff --git a/go/lib/ctrl/path_mgmt/seg_changes.go b/go/lib/ctrl/path_mgmt/seg_changes.go
deleted file mode 100644
index e14ac5f69..000000000
--- a/go/lib/ctrl/path_mgmt/seg_changes.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2018 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package path_mgmt
-
-import (
-	"fmt"
-
-	"github.com/scionproto/scion/go/lib/common"
-	"github.com/scionproto/scion/go/proto"
-)
-
-var _ proto.Cerealizable = (*SegChangesIdReq)(nil)
-
-type SegChangesIdReq struct {
-	LastCheck uint32
-}
-
-func (s *SegChangesIdReq) ProtoId() proto.ProtoIdType {
-	return proto.SegChangesIdReq_TypeID
-}
-
-func (s *SegChangesIdReq) String() string {
-	return fmt.Sprintf("LastCheck: %d", s.LastCheck)
-}
-
-type SegIds struct {
-	SegId  common.RawBytes
-	FullId common.RawBytes
-}
-
-var _ proto.Cerealizable = (*SegChangesIdReply)(nil)
-
-type SegChangesIdReply struct {
-	Ids []*SegIds
-}
-
-func (s *SegChangesIdReply) ProtoId() proto.ProtoIdType {
-	return proto.SegChangesIdReply_TypeID
-}
-
-func (s *SegChangesIdReply) String() string {
-	return fmt.Sprintf("Ids: %v", s.Ids)
-}
-
-var _ proto.Cerealizable = (*SegChangesReq)(nil)
-
-type SegChangesReq struct {
-	SegIds []common.RawBytes
-}
-
-func (s *SegChangesReq) ProtoId() proto.ProtoIdType {
-	return proto.SegChangesReq_TypeID
-}
-
-func (s *SegChangesReq) String() string {
-	return fmt.Sprintf("SegIds: %v", s.SegIds)
-}
-
-var _ proto.Cerealizable = (*SegChangesReply)(nil)
-
-type SegChangesReply struct {
-	*SegRecs
-}
diff --git a/go/lib/ctrl/path_mgmt/seg_recs.go b/go/lib/ctrl/path_mgmt/seg_recs.go
index 0cf99cf54..5e68a88af 100644
--- a/go/lib/ctrl/path_mgmt/seg_recs.go
+++ b/go/lib/ctrl/path_mgmt/seg_recs.go
@@ -67,9 +67,3 @@ var _ proto.Cerealizable = (*SegReg)(nil)
 type SegReg struct {
 	*SegRecs
 }
-
-var _ proto.Cerealizable = (*SegSync)(nil)
-
-type SegSync struct {
-	*SegRecs
-}
diff --git a/go/lib/infra/common.go b/go/lib/infra/common.go
index 1825cd1d5..512307bf0 100644
--- a/go/lib/infra/common.go
+++ b/go/lib/infra/common.go
@@ -132,15 +132,10 @@ const (
 	IfStateInfos
 	IfStateReq
 	Seg
-	SegChangesReq
-	SegChangesReply
-	SegChangesIdReq
-	SegChangesIdReply
 	SegReg
 	SegRequest
 	SegReply
 	SignedRev
-	SegSync
 	ChainRenewalRequest
 	ChainRenewalReply
 	Ack
@@ -171,14 +166,6 @@ func (mt MessageType) String() string {
 		return "IfStateReq"
 	case Seg:
 		return "Seg"
-	case SegChangesReq:
-		return "SegChangesReq"
-	case SegChangesReply:
-		return "SegChangesReply"
-	case SegChangesIdReq:
-		return "SegChangesIdReq"
-	case SegChangesIdReply:
-		return "SegChangesIdReply"
 	case SegReg:
 		return "SegReg"
 	case SegRequest:
@@ -187,8 +174,6 @@ func (mt MessageType) String() string {
 		return "SegReply"
 	case SignedRev:
 		return "SignedRev"
-	case SegSync:
-		return "SegSync"
 	case ChainRenewalRequest:
 		return "ChainRenewalRequest"
 	case ChainRenewalReply:
@@ -232,14 +217,6 @@ func (mt MessageType) MetricLabel() string {
 		return "if_info_req"
 	case Seg:
 		return "pathseg_push"
-	case SegChangesReq:
-		return "seg_changes_req"
-	case SegChangesReply:
-		return "seg_changes_push"
-	case SegChangesIdReq:
-		return "seg_changes_id_req"
-	case SegChangesIdReply:
-		return "seg_changes_id_push"
 	case SegReg:
 		return "seg_reg_push"
 	case SegRequest:
@@ -248,8 +225,6 @@ func (mt MessageType) MetricLabel() string {
 		return "seg_push"
 	case SignedRev:
 		return "revoction_push"
-	case SegSync:
-		return "seg_sync_push"
 	case ChainRenewalRequest:
 		return "chain_issue_req"
 	case ChainRenewalReply:
@@ -336,16 +311,6 @@ type Messenger interface {
 		id uint64) (*path_mgmt.SegReply, error)
 	// SendSegReply sends a reliable path_mgmt.SegReply to address a.
 	SendSegReply(ctx context.Context, msg *path_mgmt.SegReply, a net.Addr, id uint64) error
-	// SendSegSync sends a reliable path_mgmt.SegSync to address a.
-	SendSegSync(ctx context.Context, msg *path_mgmt.SegSync, a net.Addr, id uint64) error
-	GetSegChangesIds(ctx context.Context, msg *path_mgmt.SegChangesIdReq,
-		a net.Addr, id uint64) (*path_mgmt.SegChangesIdReply, error)
-	SendSegChangesIdReply(ctx context.Context,
-		msg *path_mgmt.SegChangesIdReply, a net.Addr, id uint64) error
-	GetSegChanges(ctx context.Context, msg *path_mgmt.SegChangesReq,
-		a net.Addr, id uint64) (*path_mgmt.SegChangesReply, error)
-	SendSegChangesReply(ctx context.Context,
-		msg *path_mgmt.SegChangesReply, a net.Addr, id uint64) error
 	SendHPSegReg(ctx context.Context, msg *path_mgmt.HPSegReg, a net.Addr, id uint64) error
 	GetHPSegs(ctx context.Context, msg *path_mgmt.HPSegReq, a net.Addr,
 		id uint64) (*path_mgmt.HPSegReply, error)
diff --git a/go/lib/infra/messenger/messenger.go b/go/lib/infra/messenger/messenger.go
index 40ff7490b..f73cebfd6 100644
--- a/go/lib/infra/messenger/messenger.go
+++ b/go/lib/infra/messenger/messenger.go
@@ -23,15 +23,10 @@
 //  infra.IfStateInfos        -> ctrl.SignedPld/ctrl.Pld/path_mgmt.IFStateInfos
 //  infra.IfStateReq          -> ctrl.SignedPld/ctrl.Pld/path_mgmt.IFStateReq
 //  infra.Seg                 -> ctrl.SignedPld/ctrl.Pld/seg.PathSegment
-//  infra.SegChangesReq       -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegChangesReq
-//  infra.SegChangesReply     -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegChangesReply
-//  infra.SegChangesIdReq     -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegChangesIdReq
-//  infra.SegChangesIdReply   -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegChangesIdReply
 //  infra.SegReg              -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegReg
 //  infra.SegRequest          -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegReq
 //  infra.SegReply            -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegReply
 //  infra.SignedRev           -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SignedRevInfo
-//  infra.SegSync             -> ctrl.SignedPld/ctrl.Pld/path_mgmt.SegSync
 //  infra.HPSegReg            -> ctrl.SignedPld/ctrl.Pld/path_mgmt.HPSegReg
 //  infra.HPSegRequest        -> ctrl.SignedPld/ctrl.Pld/path_mgmt.HPSegReq
 //  infra.HPSegReply          -> ctrl.SignedPld/ctrl.Pld/path_mgmt.HPSegReply
@@ -413,109 +408,6 @@ func (m *Messenger) SendSegReply(ctx context.Context, msg *path_mgmt.SegReply,
 	return m.getFallbackRequester(infra.SegReply).Notify(ctx, pld, a)
 }
 
-func (m *Messenger) SendSegSync(ctx context.Context, msg *path_mgmt.SegSync,
-	a net.Addr, id uint64) error {
-
-	pld, err := path_mgmt.NewPld(msg, nil)
-	if err != nil {
-		return err
-	}
-	return m.sendMessage(ctx, pld, a, id, infra.SegSync)
-}
-
-func (m *Messenger) GetSegChangesIds(ctx context.Context, msg *path_mgmt.SegChangesIdReq,
-	a net.Addr, id uint64) (*path_mgmt.SegChangesIdReply, error) {
-
-	logger := log.FromCtx(ctx)
-	data := &ctrl.Data{ReqId: id, TraceId: tracing.IDFromCtx(ctx)}
-	pld, err := ctrl.NewPathMgmtPld(msg, nil, data)
-	if err != nil {
-		return nil, err
-	}
-	logger.Debug("[Messenger] Sending request", "req_type", infra.SegChangesIdReq,
-		"msg_id", id, "request", msg, "peer", a)
-	replyCtrlPld, err := m.getFallbackRequester(infra.SegChangesIdReq).Request(ctx, pld, a, false)
-	if err != nil {
-		return nil, common.NewBasicError("[Messenger] Request error", err,
-			"req_type", infra.SegChangesIdReq)
-	}
-	_, replyMsg, err := Validate(replyCtrlPld)
-	if err != nil {
-		return nil, common.NewBasicError("[Messenger] Reply validation failed", err)
-	}
-	switch reply := replyMsg.(type) {
-	case *path_mgmt.SegChangesIdReply:
-		logger.Debug("[Messenger] Received reply", "req_id", id)
-		return reply, nil
-	case *ack.Ack:
-		return nil, &infra.Error{Message: reply}
-	default:
-		err := newTypeAssertErr("*path_mgmt.SegChangesIdReply", replyMsg)
-		return nil, common.NewBasicError("[Messenger] Type assertion failed", err)
-	}
-}
-
-func (m *Messenger) SendSegChangesIdReply(ctx context.Context, msg *path_mgmt.SegChangesIdReply,
-	a net.Addr, id uint64) error {
-
-	pld, err := ctrl.NewPathMgmtPld(msg, nil, &ctrl.Data{ReqId: id})
-	if err != nil {
-		return err
-	}
-	logger := log.FromCtx(ctx)
-	logger.Debug("[Messenger] Sending Notify",
-		"type", infra.SegChangesIdReply, "to", a, "id", id)
-	return m.getFallbackRequester(infra.SegChangesIdReply).Notify(ctx, pld, a)
-}
-
-func (m *Messenger) GetSegChanges(ctx context.Context, msg *path_mgmt.SegChangesReq,
-	a net.Addr, id uint64) (*path_mgmt.SegChangesReply, error) {
-
-	logger := log.FromCtx(ctx)
-	data := &ctrl.Data{ReqId: id, TraceId: tracing.IDFromCtx(ctx)}
-	pld, err := ctrl.NewPathMgmtPld(msg, nil, data)
-	if err != nil {
-		return nil, err
-	}
-	logger.Debug("[Messenger] Sending request", "req_type", infra.SegChangesReq,
-		"msg_id", id, "request", msg, "peer", a)
-	replyCtrlPld, err := m.getFallbackRequester(infra.SegChangesReq).Request(ctx, pld, a, false)
-	if err != nil {
-		return nil, common.NewBasicError("[Messenger] Request error", err,
-			"req_type", infra.SegChangesReq)
-	}
-	_, replyMsg, err := Validate(replyCtrlPld)
-	if err != nil {
-		return nil, common.NewBasicError("[Messenger] Reply validation failed", err)
-	}
-	switch reply := replyMsg.(type) {
-	case *path_mgmt.SegChangesReply:
-		if err := reply.ParseRaw(); err != nil {
-			return nil, common.NewBasicError("[Messenger] Failed to parse reply", err)
-		}
-		logger.Debug("[Messenger] Received reply", "req_id", id)
-		return reply, nil
-	case *ack.Ack:
-		return nil, &infra.Error{Message: reply}
-	default:
-		err := newTypeAssertErr("*path_mgmt.SegChangesReply", replyMsg)
-		return nil, common.NewBasicError("[Messenger] Type assertion failed", err)
-	}
-}
-
-func (m *Messenger) SendSegChangesReply(ctx context.Context, msg *path_mgmt.SegChangesReply,
-	a net.Addr, id uint64) error {
-
-	pld, err := ctrl.NewPathMgmtPld(msg, nil, &ctrl.Data{ReqId: id})
-	if err != nil {
-		return err
-	}
-	logger := log.FromCtx(ctx)
-	logger.Debug("[Messenger] Sending Notify",
-		"type", infra.SegChangesReply, "to", a, "id", id)
-	return m.getFallbackRequester(infra.SegChangesReply).Notify(ctx, pld, a)
-}
-
 func (m *Messenger) SendHPSegReg(ctx context.Context, msg *path_mgmt.HPSegReg, a net.Addr,
 	id uint64) error {
 
@@ -1100,22 +992,12 @@ func Validate(pld *ctrl.Pld) (infra.MessageType, proto.Cerealizable, error) {
 			return infra.SegReply, pld.PathMgmt.SegReply, nil
 		case proto.PathMgmt_Which_segReg:
 			return infra.SegReg, pld.PathMgmt.SegReg, nil
-		case proto.PathMgmt_Which_segSync:
-			return infra.SegSync, pld.PathMgmt.SegSync, nil
 		case proto.PathMgmt_Which_sRevInfo:
 			return infra.SignedRev, pld.PathMgmt.SRevInfo, nil
 		case proto.PathMgmt_Which_ifStateReq:
 			return infra.IfStateReq, pld.PathMgmt.IFStateReq, nil
 		case proto.PathMgmt_Which_ifStateInfos:
 			return infra.IfStateInfos, pld.PathMgmt.IFStateInfos, nil
-		case proto.PathMgmt_Which_segChangesIdReq:
-			return infra.SegChangesIdReq, pld.PathMgmt.SegChangesIdReq, nil
-		case proto.PathMgmt_Which_segChangesIdReply:
-			return infra.SegChangesIdReply, pld.PathMgmt.SegChangesIdReply, nil
-		case proto.PathMgmt_Which_segChangesReq:
-			return infra.SegChangesReq, pld.PathMgmt.SegChangesReq, nil
-		case proto.PathMgmt_Which_segChangesReply:
-			return infra.SegChangesReply, pld.PathMgmt.SegChangesReply, nil
 		case proto.PathMgmt_Which_hpSegReq:
 			return infra.HPSegRequest, pld.PathMgmt.HPSegReq, nil
 		case proto.PathMgmt_Which_hpSegReply:
diff --git a/go/lib/infra/mock_infra/infra.go b/go/lib/infra/mock_infra/infra.go
index 8eef2baf4..90e7815a2 100644
--- a/go/lib/infra/mock_infra/infra.go
+++ b/go/lib/infra/mock_infra/infra.go
@@ -114,36 +114,6 @@ func (mr *MockMessengerMockRecorder) GetHPSegs(arg0, arg1, arg2, arg3 interface{
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHPSegs", reflect.TypeOf((*MockMessenger)(nil).GetHPSegs), arg0, arg1, arg2, arg3)
 }
 
-// GetSegChanges mocks base method
-func (m *MockMessenger) GetSegChanges(arg0 context.Context, arg1 *path_mgmt.SegChangesReq, arg2 net.Addr, arg3 uint64) (*path_mgmt.SegChangesReply, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetSegChanges", arg0, arg1, arg2, arg3)
-	ret0, _ := ret[0].(*path_mgmt.SegChangesReply)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetSegChanges indicates an expected call of GetSegChanges
-func (mr *MockMessengerMockRecorder) GetSegChanges(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegChanges", reflect.TypeOf((*MockMessenger)(nil).GetSegChanges), arg0, arg1, arg2, arg3)
-}
-
-// GetSegChangesIds mocks base method
-func (m *MockMessenger) GetSegChangesIds(arg0 context.Context, arg1 *path_mgmt.SegChangesIdReq, arg2 net.Addr, arg3 uint64) (*path_mgmt.SegChangesIdReply, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "GetSegChangesIds", arg0, arg1, arg2, arg3)
-	ret0, _ := ret[0].(*path_mgmt.SegChangesIdReply)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// GetSegChangesIds indicates an expected call of GetSegChangesIds
-func (mr *MockMessengerMockRecorder) GetSegChangesIds(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSegChangesIds", reflect.TypeOf((*MockMessenger)(nil).GetSegChangesIds), arg0, arg1, arg2, arg3)
-}
-
 // GetSegs mocks base method
 func (m *MockMessenger) GetSegs(arg0 context.Context, arg1 *path_mgmt.SegReq, arg2 net.Addr, arg3 uint64) (*path_mgmt.SegReply, error) {
 	m.ctrl.T.Helper()
@@ -341,34 +311,6 @@ func (mr *MockMessengerMockRecorder) SendRev(arg0, arg1, arg2, arg3 interface{})
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendRev", reflect.TypeOf((*MockMessenger)(nil).SendRev), arg0, arg1, arg2, arg3)
 }
 
-// SendSegChangesIdReply mocks base method
-func (m *MockMessenger) SendSegChangesIdReply(arg0 context.Context, arg1 *path_mgmt.SegChangesIdReply, arg2 net.Addr, arg3 uint64) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendSegChangesIdReply", arg0, arg1, arg2, arg3)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// SendSegChangesIdReply indicates an expected call of SendSegChangesIdReply
-func (mr *MockMessengerMockRecorder) SendSegChangesIdReply(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSegChangesIdReply", reflect.TypeOf((*MockMessenger)(nil).SendSegChangesIdReply), arg0, arg1, arg2, arg3)
-}
-
-// SendSegChangesReply mocks base method
-func (m *MockMessenger) SendSegChangesReply(arg0 context.Context, arg1 *path_mgmt.SegChangesReply, arg2 net.Addr, arg3 uint64) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendSegChangesReply", arg0, arg1, arg2, arg3)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// SendSegChangesReply indicates an expected call of SendSegChangesReply
-func (mr *MockMessengerMockRecorder) SendSegChangesReply(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSegChangesReply", reflect.TypeOf((*MockMessenger)(nil).SendSegChangesReply), arg0, arg1, arg2, arg3)
-}
-
 // SendSegReg mocks base method
 func (m *MockMessenger) SendSegReg(arg0 context.Context, arg1 *path_mgmt.SegReg, arg2 net.Addr, arg3 uint64) error {
 	m.ctrl.T.Helper()
@@ -397,20 +339,6 @@ func (mr *MockMessengerMockRecorder) SendSegReply(arg0, arg1, arg2, arg3 interfa
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSegReply", reflect.TypeOf((*MockMessenger)(nil).SendSegReply), arg0, arg1, arg2, arg3)
 }
 
-// SendSegSync mocks base method
-func (m *MockMessenger) SendSegSync(arg0 context.Context, arg1 *path_mgmt.SegSync, arg2 net.Addr, arg3 uint64) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "SendSegSync", arg0, arg1, arg2, arg3)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// SendSegSync indicates an expected call of SendSegSync
-func (mr *MockMessengerMockRecorder) SendSegSync(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendSegSync", reflect.TypeOf((*MockMessenger)(nil).SendSegSync), arg0, arg1, arg2, arg3)
-}
-
 // SendTRC mocks base method
 func (m *MockMessenger) SendTRC(arg0 context.Context, arg1 *cert_mgmt.TRC, arg2 net.Addr, arg3 uint64) error {
 	m.ctrl.T.Helper()
diff --git a/go/lib/infra/modules/segfetcher/BUILD.bazel b/go/lib/infra/modules/segfetcher/BUILD.bazel
index 26dfdbc7a..36e12440c 100644
--- a/go/lib/infra/modules/segfetcher/BUILD.bazel
+++ b/go/lib/infra/modules/segfetcher/BUILD.bazel
@@ -17,7 +17,6 @@ go_library(
     visibility = ["//visibility:public"],
     deps = [
         "//go/lib/addr:go_default_library",
-        "//go/lib/common:go_default_library",
         "//go/lib/ctrl/path_mgmt:go_default_library",
         "//go/lib/ctrl/seg:go_default_library",
         "//go/lib/infra:go_default_library",
diff --git a/go/lib/infra/modules/segfetcher/fetcher.go b/go/lib/infra/modules/segfetcher/fetcher.go
index bdf6dcc44..658fbe595 100644
--- a/go/lib/infra/modules/segfetcher/fetcher.go
+++ b/go/lib/infra/modules/segfetcher/fetcher.go
@@ -20,8 +20,6 @@ import (
 	"net"
 	"time"
 
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher/internal/metrics"
@@ -39,9 +37,8 @@ const (
 
 // errors for metrics classification.
 var (
-	errValidate = serrors.New("request validation failed")
-	errFetch    = serrors.New("fetching failed")
-	errDB       = serrors.New("error with the db")
+	errFetch = serrors.New("fetching failed")
+	errDB    = serrors.New("error with the db")
 )
 
 // ReplyHandler handles replies.
@@ -55,8 +52,6 @@ type FetcherConfig struct {
 	// QueryInterval specifies after how much time segments should be
 	// refetched at the remote server.
 	QueryInterval time.Duration
-	// LocalIA is the IA this process is in.
-	LocalIA addr.IA
 	// Verifier is the verifier to use.
 	Verifier infra.Verifier
 	// PathDB is the path db to use.
@@ -67,8 +62,6 @@ type FetcherConfig struct {
 	RequestAPI RequestAPI
 	// DstProvider provides destinations to fetch segments from
 	DstProvider DstProvider
-	// Splitter is used to split requests.
-	Splitter Splitter
 	// MetricsNamespace is the namespace used for metrics.
 	MetricsNamespace string
 	// LocalInfo provides information about local segments.
@@ -78,7 +71,6 @@ type FetcherConfig struct {
 // New creates a new fetcher from the configuration.
 func (cfg FetcherConfig) New() *Fetcher {
 	return &Fetcher{
-		Splitter:  cfg.Splitter,
 		Resolver:  NewResolver(cfg.PathDB, cfg.RevCache, cfg.LocalInfo),
 		Requester: &DefaultRequester{API: cfg.RequestAPI, DstProvider: cfg.DstProvider},
 		ReplyHandler: &seghandler.Handler{
@@ -91,9 +83,8 @@ func (cfg FetcherConfig) New() *Fetcher {
 	}
 }
 
-// Fetcher fetches, verifies and stores segments for a given path request.
+// Fetcher fetches, verifies and stores segments for a path segment request.
 type Fetcher struct {
-	Splitter      Splitter
 	Resolver      Resolver
 	Requester     Requester
 	ReplyHandler  ReplyHandler
@@ -102,54 +93,39 @@ type Fetcher struct {
 	metrics       metrics.Fetcher
 }
 
-// FetchSegs fetches the required segments to build a path between src and dst
-// of the request. First the request is validated and then depending on the
-// cache the segments are fetched from the remote server.
-func (f *Fetcher) FetchSegs(ctx context.Context, req Request) (Segments, error) {
-	reqSet, err := f.Splitter.Split(ctx, req)
+// Fetch loads the requested segments from the path DB or requests them from a remote path server.
+func (f *Fetcher) Fetch(ctx context.Context, reqs Requests, refresh bool) (Segments, error) {
+	// Load local and cached segments from DB
+	loadedSegs, fetchReqs, err := f.Resolver.Resolve(ctx, reqs, refresh)
 	if err != nil {
-		return Segments{}, err
+		return Segments{}, serrors.Wrap(errDB, err)
 	}
-	var segs Segments
-	for i := 0; i < 3; i++ {
-		log.FromCtx(ctx).Debug("Request to process", "req", reqSet, "segs", segs, "iteration", i+1)
-		segs, reqSet, err = f.Resolver.Resolve(ctx, segs, reqSet)
-		if err != nil {
-			return Segments{}, serrors.Wrap(errDB, err)
-		}
-		log.FromCtx(ctx).Debug("After resolving", "req", reqSet, "segs", segs, "iteration", i+1)
-		if reqSet.IsLoaded() {
-			break
-		}
-		// in 3 iteration (i==2) everything should be resolved, worst case:
-		// 1 iteration: up & down segment fetched.
-		// 2 iteration: up & down resolved, core fetched.
-		// 3 iteration: core resolved -> done.
-		if i >= 2 {
-			return segs, common.NewBasicError(
-				"Segment lookup not done in expected amount of iterations (implementation bug)",
-				nil, "iterations", i+1)
-		}
-		// XXX(lukedirtwalker): Optimally we wouldn't need a different timeout
-		// here. The problem is that revocations can't be differentiated from
-		// timeouts. And having 10s timeouts plays really bad together with
-		// revocations. See also: https://github.com/scionproto/scion/issues/3052
-		reqCtx, cancelF := context.WithTimeout(ctx, 3*time.Second)
-		reqCtx = log.CtxWith(reqCtx, log.FromCtx(ctx))
-		replies := f.Requester.Request(reqCtx, reqSet)
-		// TODO(lukedirtwalker): We need to have early trigger for the last request.
-		if reqSet, err = f.waitOnProcessed(ctx, replies, reqSet); err != nil {
-			cancelF()
-			return Segments{}, err
-		}
-		cancelF()
+	if len(fetchReqs) == 0 {
+		return loadedSegs, nil
 	}
-	return segs, nil
+	// Forward and cache any requests that were not local / cached
+	fetchedSegs, err := f.Request(ctx, fetchReqs)
+	if err != nil {
+		return Segments{}, serrors.Wrap(errFetch, err)
+	}
+	return append(loadedSegs, fetchedSegs...), nil
+}
+
+func (f *Fetcher) Request(ctx context.Context, reqs Requests) (Segments, error) {
+	// XXX(lukedirtwalker): Optimally we wouldn't need a different timeout
+	// here. The problem is that revocations can't be differentiated from
+	// timeouts. And having 10s timeouts plays really bad together with
+	// revocations. See also: https://github.com/scionproto/scion/issues/3052
+	reqCtx, cancelF := context.WithTimeout(ctx, 3*time.Second)
+	defer cancelF()
+	replies := f.Requester.Request(reqCtx, reqs)
+	return f.waitOnProcessed(ctx, replies)
 }
 
-func (f *Fetcher) waitOnProcessed(ctx context.Context, replies <-chan ReplyOrErr,
-	reqSet RequestSet) (RequestSet, error) {
+func (f *Fetcher) waitOnProcessed(ctx context.Context,
+	replies <-chan ReplyOrErr) (Segments, error) {
 
+	var segs Segments
 	logger := log.FromCtx(ctx)
 	for reply := range replies {
 		// TODO(lukedirtwalker): Should we do this in go routines?
@@ -158,7 +134,6 @@ func (f *Fetcher) waitOnProcessed(ctx context.Context, replies <-chan ReplyOrErr
 			log.FromCtx(ctx).Info("Request for unreachable dest ignored",
 				"req", reply.Req, "err", reply.Err)
 			f.metrics.SegRequests(labels.WithResult(metrics.OkSuccess)).Inc()
-			reqSet = updateRequestState(reqSet, reply.Req, Loaded)
 			continue
 		}
 		if reply.Err != nil {
@@ -166,11 +141,10 @@ func (f *Fetcher) waitOnProcessed(ctx context.Context, replies <-chan ReplyOrErr
 				labels.Result = metrics.ErrTimeout
 			}
 			f.metrics.SegRequests(labels).Inc()
-			return reqSet, reply.Err
+			return segs, reply.Err
 		}
 		if reply.Reply == nil || reply.Reply.Recs == nil {
 			f.metrics.SegRequests(labels.WithResult(metrics.OkSuccess)).Inc()
-			reqSet = updateRequestState(reqSet, reply.Req, Fetched)
 			continue
 		}
 		r := f.ReplyHandler.Handle(ctx, replyToRecs(reply.Reply), reply.Peer, nil)
@@ -180,14 +154,14 @@ func (f *Fetcher) waitOnProcessed(ctx context.Context, replies <-chan ReplyOrErr
 				r.Stats().RevDBErrs(), r.Stats().RevVerifyErrors())
 			if err := r.Err(); err != nil {
 				f.metrics.SegRequests(labels.WithResult(metrics.ErrProcess)).Inc()
-				return reqSet, err
+				return segs, err
 			}
 			if len(r.VerificationErrors()) > 0 {
 				log.FromCtx(ctx).Info("Error during verification of segments/revocations",
 					"errors", r.VerificationErrors().ToError())
 			}
-			reqSet = updateRequestState(reqSet, reply.Req, Fetched)
-			nextQuery := f.nextQuery(r.Stats().VerifiedSegs)
+			segs = append(segs, segsWithHPToSegs(r.Stats().VerifiedSegs)...)
+			nextQuery := f.nextQuery(segs)
 			_, err := f.PathDB.InsertNextQuery(ctx, reply.Req.Src, reply.Req.Dst, nil, nextQuery)
 			if err != nil {
 				logger.Info("NextQuery insertion failed", "err", err)
@@ -195,15 +169,15 @@ func (f *Fetcher) waitOnProcessed(ctx context.Context, replies <-chan ReplyOrErr
 			f.metrics.SegRequests(labels.WithResult(metrics.OkSuccess)).Inc()
 		case <-ctx.Done():
 			f.metrics.SegRequests(labels.WithResult(metrics.ErrTimeout)).Inc()
-			return reqSet, ctx.Err()
+			return segs, ctx.Err()
 		}
 	}
-	return reqSet, nil
+	return segs, nil
 }
 
 // nextQuery decides the next time a query should be issued based on the
 // received segments.
-func (f *Fetcher) nextQuery(segs []*seghandler.SegWithHP) time.Time {
+func (f *Fetcher) nextQuery(segs Segments) time.Time {
 	// Determine the lead time for the latest segment expiration.
 	// We want to request new segments before the last one has expired.
 	expirationLead := maxSegmentExpiry(segs).Add(-expirationLeadTime)
@@ -222,10 +196,10 @@ func (f *Fetcher) nearestNextQueryTime(now, nextQuery time.Time) time.Time {
 	return nextQuery
 }
 
-func maxSegmentExpiry(segs []*seghandler.SegWithHP) time.Time {
+func maxSegmentExpiry(segs Segments) time.Time {
 	var max time.Time
 	for _, seg := range segs {
-		if exp := seg.Seg.Segment.MinExpiry(); exp.After(max) {
+		if exp := seg.Segment.MinExpiry(); exp.After(max) {
 			max = exp
 		}
 	}
@@ -239,17 +213,10 @@ func replyToRecs(reply *path_mgmt.SegReply) seghandler.Segments {
 	}
 }
 
-func updateRequestState(reqSet RequestSet, reqToUpdate Request, newState RequestState) RequestSet {
-	if reqSet.Up.EqualAddr(reqToUpdate) {
-		reqSet.Up.State = newState
-	} else if reqSet.Down.EqualAddr(reqToUpdate) {
-		reqSet.Down.State = newState
-	} else {
-		for i, coreReq := range reqSet.Cores {
-			if coreReq.EqualAddr(reqToUpdate) {
-				reqSet.Cores[i].State = newState
-			}
-		}
+func segsWithHPToSegs(segsWithHP []*seghandler.SegWithHP) Segments {
+	segs := make(Segments, 0, len(segsWithHP))
+	for _, seg := range segsWithHP {
+		segs = append(segs, seg.Seg)
 	}
-	return reqSet
+	return segs
 }
diff --git a/go/lib/infra/modules/segfetcher/metrics.go b/go/lib/infra/modules/segfetcher/metrics.go
index da588cce4..9e044c839 100644
--- a/go/lib/infra/modules/segfetcher/metrics.go
+++ b/go/lib/infra/modules/segfetcher/metrics.go
@@ -32,8 +32,6 @@ func ErrToMetricsLabel(err error) string {
 		return prom.ErrDB
 	case errors.Is(err, errFetch):
 		return prom.ErrNetwork
-	case errors.Is(err, errValidate):
-		return prom.ErrParse
 	case errors.Is(err, seghandler.ErrVerification):
 		return prom.ErrVerify
 	default:
diff --git a/go/lib/infra/modules/segfetcher/mock_segfetcher/segfetcher.go b/go/lib/infra/modules/segfetcher/mock_segfetcher/segfetcher.go
index 4f5e7d6d6..8e8888217 100644
--- a/go/lib/infra/modules/segfetcher/mock_segfetcher/segfetcher.go
+++ b/go/lib/infra/modules/segfetcher/mock_segfetcher/segfetcher.go
@@ -114,7 +114,7 @@ func (m *MockRequester) EXPECT() *MockRequesterMockRecorder {
 }
 
 // Request mocks base method
-func (m *MockRequester) Request(arg0 context.Context, arg1 segfetcher.RequestSet) <-chan segfetcher.ReplyOrErr {
+func (m *MockRequester) Request(arg0 context.Context, arg1 segfetcher.Requests) <-chan segfetcher.ReplyOrErr {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Request", arg0, arg1)
 	ret0, _ := ret[0].(<-chan segfetcher.ReplyOrErr)
@@ -189,11 +189,11 @@ func (m *MockResolver) EXPECT() *MockResolverMockRecorder {
 }
 
 // Resolve mocks base method
-func (m *MockResolver) Resolve(arg0 context.Context, arg1 segfetcher.Segments, arg2 segfetcher.RequestSet) (segfetcher.Segments, segfetcher.RequestSet, error) {
+func (m *MockResolver) Resolve(arg0 context.Context, arg1 segfetcher.Requests, arg2 bool) (segfetcher.Segments, segfetcher.Requests, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Resolve", arg0, arg1, arg2)
 	ret0, _ := ret[0].(segfetcher.Segments)
-	ret1, _ := ret[1].(segfetcher.RequestSet)
+	ret1, _ := ret[1].(segfetcher.Requests)
 	ret2, _ := ret[2].(error)
 	return ret0, ret1, ret2
 }
@@ -228,10 +228,10 @@ func (m *MockSplitter) EXPECT() *MockSplitterMockRecorder {
 }
 
 // Split mocks base method
-func (m *MockSplitter) Split(arg0 context.Context, arg1 segfetcher.Request) (segfetcher.RequestSet, error) {
+func (m *MockSplitter) Split(arg0 context.Context, arg1 addr.IA) (segfetcher.Requests, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Split", arg0, arg1)
-	ret0, _ := ret[0].(segfetcher.RequestSet)
+	ret0, _ := ret[0].(segfetcher.Requests)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -266,16 +266,15 @@ func (m *MockLocalInfo) EXPECT() *MockLocalInfoMockRecorder {
 }
 
 // IsSegLocal mocks base method
-func (m *MockLocalInfo) IsSegLocal(arg0 context.Context, arg1, arg2 addr.IA) (bool, error) {
+func (m *MockLocalInfo) IsSegLocal(arg0 segfetcher.Request) bool {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "IsSegLocal", arg0, arg1, arg2)
+	ret := m.ctrl.Call(m, "IsSegLocal", arg0)
 	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
+	return ret0
 }
 
 // IsSegLocal indicates an expected call of IsSegLocal
-func (mr *MockLocalInfoMockRecorder) IsSegLocal(arg0, arg1, arg2 interface{}) *gomock.Call {
+func (mr *MockLocalInfoMockRecorder) IsSegLocal(arg0 interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSegLocal", reflect.TypeOf((*MockLocalInfo)(nil).IsSegLocal), arg0, arg1, arg2)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsSegLocal", reflect.TypeOf((*MockLocalInfo)(nil).IsSegLocal), arg0)
 }
diff --git a/go/lib/infra/modules/segfetcher/pather.go b/go/lib/infra/modules/segfetcher/pather.go
index 30e3e6915..2574ce018 100644
--- a/go/lib/infra/modules/segfetcher/pather.go
+++ b/go/lib/infra/modules/segfetcher/pather.go
@@ -23,10 +23,10 @@ import (
 	"github.com/scionproto/scion/go/lib/ctrl/seg"
 	"github.com/scionproto/scion/go/lib/infra/modules/combinator"
 	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/revcache"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/topology"
+	"github.com/scionproto/scion/go/proto"
 )
 
 // Pather errors.
@@ -38,10 +38,10 @@ var (
 // Pather is used to construct paths from the path database. If necessary, paths
 // are fetched over the network.
 type Pather struct {
-	PathDB       pathdb.PathDB
-	RevCache     revcache.RevCache
 	TopoProvider topology.Provider
+	RevCache     revcache.RevCache
 	Fetcher      *Fetcher
+	Splitter     Splitter
 	HeaderV2     bool
 }
 
@@ -60,11 +60,11 @@ func (p *Pather) GetPaths(ctx context.Context, dst addr.IA,
 		// For AS local communication, an empty path is used.
 		return []*combinator.Path{{}}, nil
 	}
-	req := Request{Src: src, Dst: dst}
-	if refresh {
-		req.State = Fetch
+	reqs, err := p.Splitter.Split(ctx, dst)
+	if err != nil {
+		return nil, err
 	}
-	segs, err := p.Fetcher.FetchSegs(ctx, req)
+	segs, err := p.Fetcher.Fetch(ctx, reqs, refresh)
 	if err != nil {
 		return nil, err
 	}
@@ -80,10 +80,11 @@ func (p *Pather) GetPaths(ctx context.Context, dst addr.IA,
 }
 
 func (p *Pather) buildAllPaths(src, dst addr.IA, segs Segments) []*combinator.Path {
-	destinations := p.findDestinations(dst, segs.Up, segs.Core)
+	up, core, down := categorizeSegs(segs)
+	destinations := p.findDestinations(dst, up, core)
 	var paths []*combinator.Path
 	for dst := range destinations {
-		paths = append(paths, combinator.Combine(src, dst, segs.Up, segs.Core, segs.Down)...)
+		paths = append(paths, combinator.Combine(src, dst, up, core, down)...)
 	}
 	// Filter expired paths
 	now := time.Now()
@@ -140,3 +141,19 @@ func (p *Pather) filterRevoked(ctx context.Context,
 	}
 	return newPaths, nil
 }
+
+// categorizeSegs splits a flat list of segments with type info into one
+// separate list per segment type.
+func categorizeSegs(segs Segments) (up, core, down seg.Segments) {
+	for _, seg := range segs {
+		switch seg.Type {
+		case proto.PathSegType_up:
+			up = append(up, seg.Segment)
+		case proto.PathSegType_core:
+			core = append(core, seg.Segment)
+		case proto.PathSegType_down:
+			down = append(down, seg.Segment)
+		}
+	}
+	return
+}
diff --git a/go/lib/infra/modules/segfetcher/request.go b/go/lib/infra/modules/segfetcher/request.go
index 995d9c994..0c1b2feed 100644
--- a/go/lib/infra/modules/segfetcher/request.go
+++ b/go/lib/infra/modules/segfetcher/request.go
@@ -17,30 +17,14 @@ package segfetcher
 import (
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
-)
-
-// RequestState is the state the request is in.
-type RequestState int
-
-const (
-	// Unresolved means the request is not yet resolved.
-	Unresolved RequestState = iota
-	// Fetch means the request needs to be fetched.
-	Fetch
-	// Cached means the request should be cached locally and can be loaded from
-	// DB.
-	Cached
-	// Fetched means the request has been fetched and should be in the DB.
-	Fetched
-	// Loaded means the request has been loaded from the DB.
-	Loaded
+	"github.com/scionproto/scion/go/proto"
 )
 
 // Request represents a path or segment request.
 type Request struct {
-	Src   addr.IA
-	Dst   addr.IA
-	State RequestState
+	Src     addr.IA
+	Dst     addr.IA
+	SegType proto.PathSegType
 }
 
 // IsZero returns whether the request is empty.
@@ -56,40 +40,9 @@ func (r Request) ToSegReq() *path_mgmt.SegReq {
 	}
 }
 
-// EqualAddr returns whether the two request refer to the same src/dst.
-func (r Request) EqualAddr(other Request) bool {
-	return r.Src.Equal(other.Src) && r.Dst.Equal(other.Dst)
-}
-
-// RequestSet is a set of requests.
-type RequestSet struct {
-	Up    Request
-	Cores Requests
-	Down  Request
-	// Fetch indicates the request should always be fetched from remote,
-	// regardless of whether is is cached.
-	Fetch bool
-}
-
-// IsLoaded returns true if all non-zero requests in the set are in state
-// loaded.
-func (r RequestSet) IsLoaded() bool {
-	return (r.Up.IsZero() || r.Up.State == Loaded) &&
-		(r.Down.IsZero() || r.Down.State == Loaded) &&
-		r.Cores.AllLoaded()
-}
-
-func (r RequestSet) resolveUp() bool {
-	return !r.Up.IsZero() && (r.Up.State == Unresolved || r.Up.State == Fetched)
-}
-
-func (r RequestSet) resolveDown() bool {
-	return !r.Down.IsZero() && (r.Down.State == Unresolved || r.Down.State == Fetched)
-}
-
-func (r RequestSet) upDownResolved() bool {
-	return (r.Up.IsZero() || r.Up.State == Loaded) &&
-		(r.Down.IsZero() || r.Down.State == Loaded)
+// Equal returns whether the two request refer to the same src/dst/type.
+func (r Request) Equal(other Request) bool {
+	return r.Src.Equal(other.Src) && r.Dst.Equal(other.Dst) && r.SegType == other.SegType
 }
 
 // Requests is a list of requests and provides some convenience methods on top
@@ -111,16 +64,6 @@ func (r Requests) IsEmpty() bool {
 	return len(r) == 0
 }
 
-// AllLoaded returns whether all entries in request have state loaded.
-func (r Requests) AllLoaded() bool {
-	for _, req := range r {
-		if req.State != Loaded {
-			return false
-		}
-	}
-	return true
-}
-
 func (r Requests) extractIAs(extract func(Request) addr.IA) []addr.IA {
 	set := make(map[addr.IA]struct{})
 	for _, req := range r {
diff --git a/go/lib/infra/modules/segfetcher/requester.go b/go/lib/infra/modules/segfetcher/requester.go
index 05f5961d5..40a748e81 100644
--- a/go/lib/infra/modules/segfetcher/requester.go
+++ b/go/lib/infra/modules/segfetcher/requester.go
@@ -49,7 +49,7 @@ type ReplyOrErr struct {
 
 // Requester requests segments.
 type Requester interface {
-	Request(ctx context.Context, req RequestSet) <-chan ReplyOrErr
+	Request(ctx context.Context, req Requests) <-chan ReplyOrErr
 }
 
 // DefaultRequester requests all segments that can be requested from a request set.
@@ -58,19 +58,9 @@ type DefaultRequester struct {
 	DstProvider DstProvider
 }
 
-// Request all requests in the request set that are in fetch state.
-func (r *DefaultRequester) Request(ctx context.Context, req RequestSet) <-chan ReplyOrErr {
-	var reqs Requests
-	allReqs := append(Requests{req.Up, req.Down}, req.Cores...)
-	for _, req := range allReqs {
-		if req.State == Fetch {
-			reqs = append(reqs, req)
-		}
-	}
-	return r.fetchReqs(ctx, reqs)
-}
+// Request all requests in the request set
+func (r *DefaultRequester) Request(ctx context.Context, reqs Requests) <-chan ReplyOrErr {
 
-func (r *DefaultRequester) fetchReqs(ctx context.Context, reqs Requests) <-chan ReplyOrErr {
 	replies := make(chan ReplyOrErr, len(reqs))
 	var wg sync.WaitGroup
 	for i := range reqs {
diff --git a/go/lib/infra/modules/segfetcher/resolver.go b/go/lib/infra/modules/segfetcher/resolver.go
index 3070865aa..dcc6f243a 100644
--- a/go/lib/infra/modules/segfetcher/resolver.go
+++ b/go/lib/infra/modules/segfetcher/resolver.go
@@ -32,22 +32,20 @@ var ErrInvalidRequest = serrors.New("invalid request")
 
 // Resolver resolves segments that are locally cached.
 type Resolver interface {
-	// Resolve resolves a request set. It returns the segments that are locally
-	// stored and the set of requests that have to be requested at a remote server.
-	Resolve(ctx context.Context, segs Segments, req RequestSet) (Segments, RequestSet, error)
+	// Resolve resolves requests. It loads the segments that are locally available
+	// from the DB and returns the set of requests that have to be requested at a
+	// remote server.
+	Resolve(ctx context.Context, reqs Requests, refresh bool) (Segments, Requests, error)
 }
 
 // LocalInfo provides information about which segments are always locally
 // stored.
 type LocalInfo interface {
-	// IsSegLocal returns whether this segment should always be locally cached.
-	IsSegLocal(ctx context.Context, src, dst addr.IA) (bool, error)
+	// IsSegLocal returns whether the requested segment is always locally stored.
+	IsSegLocal(req Request) bool
 }
 
-// NewResolver creates a new resolver with the given DB. The DB might be
-// customized. E.g. a PS could inject a wrapper around GetNextQuery so that it
-// always returns that the cache is up to date for segments that should be
-// available local.
+// NewResolver creates a new resolver with the given DB.
 func NewResolver(DB pathdb.Read, revCache revcache.RevCache, localInfo LocalInfo) *DefaultResolver {
 	return &DefaultResolver{
 		DB:        DB,
@@ -63,288 +61,79 @@ type DefaultResolver struct {
 	LocalInfo LocalInfo
 }
 
-// Resolve resolves a request set. It returns the segments that are locally
-// stored and the set of requests that have to be requested at a remote server.
-func (r *DefaultResolver) Resolve(ctx context.Context, segs Segments,
-	req RequestSet) (Segments, RequestSet, error) {
+// Resolve resolves requests. It loads the segments that are locally available
+// from the DB and returns the set of requests that have to be requested at a
+// remote server.
+func (r *DefaultResolver) Resolve(ctx context.Context,
+	reqs Requests, refresh bool) (Segments, Requests, error) {
 
-	var err error
-	if req.resolveUp() {
-		if segs, req, err = r.resolveUpSegs(ctx, segs, req); err != nil {
-			return segs, req, err
-		}
-	}
-	if req.resolveDown() {
-		if segs, req, err = r.resolveDownSegs(ctx, segs, req); err != nil {
-			return segs, req, err
-		}
-	}
-	if zeroUpDownSegsCached(req, segs) {
-		for i := range req.Cores {
-			req.Cores[i].State = Loaded
-		}
-		return segs, req, nil
-	}
-	// If there are still up or down segments to request, or if there are no
-	// core segments no more action can be done here.
-	if !req.upDownResolved() || req.Cores.IsEmpty() {
-		return segs, req, nil
-	}
-	// now resolve core segs:
-	req.Cores, err = r.expandCores(segs, req)
-	if err != nil {
-		return segs, req, err
-	}
-	if req.Cores, err = r.resolveCores(ctx, req); err != nil {
-		return segs, req, err
-	}
-	if len(req.Cores) == 0 {
-		req.Cores = nil
-	}
-	for i, coreReq := range req.Cores {
-		if local, err := r.LocalInfo.IsSegLocal(ctx, coreReq.Src, coreReq.Dst); err != nil {
-			return segs, req, err
-		} else if local {
-			coreReq.State = Cached
-		}
-		if coreReq.State != Cached && coreReq.State != Fetched {
-			continue
-		}
-		coreRes, err := r.DB.Get(ctx, &query.Params{
-			StartsAt: []addr.IA{coreReq.Dst},
-			EndsAt:   []addr.IA{coreReq.Src},
-			SegTypes: []proto.PathSegType{proto.PathSegType_core},
-		})
-		if err != nil {
-			return segs, req, err
-		}
-		allRev, err := r.allRevoked(ctx, coreRes)
+	var segs Segments
+	var fetchReqs Requests
+	for i := range reqs {
+		segsi, err := r.resolveSegment(ctx, reqs[i], refresh)
 		if err != nil {
-			return segs, req, err
+			return nil, nil, err
 		}
-		if allRev && coreReq.State != Fetched {
-			req.Cores[i].State = Fetch
+		if segsi != nil {
+			segs = append(segs, segsi...)
 		} else {
-			req.Cores[i].State = Loaded
+			fetchReqs = append(fetchReqs, reqs[i])
 		}
-		segs.Core = append(segs.Core, coreRes.Segs()...)
 	}
-	return segs, req, nil
-}
-
-func (r *DefaultResolver) resolveUpSegs(ctx context.Context, segs Segments,
-	req RequestSet) (Segments, RequestSet, error) {
-
-	if req.Fetch && req.Up.State == Unresolved {
-		req.Up.State = Fetch
-		return segs, req, nil
-	}
-	var err error
-	segs.Up, req.Up, err = r.resolveSegment(ctx, req.Up, false)
-	return segs, req, err
-}
-
-func (r *DefaultResolver) resolveDownSegs(ctx context.Context, segs Segments,
-	req RequestSet) (Segments, RequestSet, error) {
-
-	if req.Fetch && req.Down.State == Unresolved {
-		req.Down.State = Fetch
-		return segs, req, nil
-	}
-	var err error
-	segs.Down, req.Down, err = r.resolveSegment(ctx, req.Down, true)
-	return segs, req, err
+	return segs, fetchReqs, nil
 }
 
+// resolveSegment loads the segments for this request from the DB.
+// Returns nil if the segments are not local information and are not
+// available/up to date from the cache.
 func (r *DefaultResolver) resolveSegment(ctx context.Context,
-	req Request, consDir bool) (seg.Segments, Request, error) {
+	req Request, refresh bool) (Segments, error) {
 
-	if local, err := r.LocalInfo.IsSegLocal(ctx, req.Src, req.Dst); err != nil {
-		return nil, req, err
-	} else if local {
-		req.State = Cached
-	}
-	if req.State == Unresolved {
+	local := r.LocalInfo.IsSegLocal(req)
+	if !local {
+		if refresh {
+			return nil, nil
+		}
 		fetch, err := r.needsFetching(ctx, req)
 		if err != nil || fetch {
-			req.State = Fetch
-			return nil, req, err
+			return nil, err
 		}
 	}
-	start, end := req.Src, req.Dst
-	segType := proto.PathSegType_down
-	if !consDir {
-		start, end = end, start
-		segType = proto.PathSegType_up
-	}
-	res, err := r.DB.Get(ctx, &query.Params{
-		StartsAt: []addr.IA{start},
-		EndsAt:   []addr.IA{end},
-		SegTypes: []proto.PathSegType{segType},
-	})
+	// The segment is local or cached
+	res, err := r.loadSegment(ctx, req)
 	if err != nil {
-		return nil, req, err
+		return nil, err
 	}
 	allRev, err := r.allRevoked(ctx, res)
 	if err != nil {
-		return res.Segs(), req, err
+		return nil, err
 	}
 	// because of revocations our cache is empty, so refetch
-	if allRev && req.State == Unresolved {
-		req.State = Fetch
-		return nil, req, err
+	if allRev && !local {
+		return nil, nil
 	}
-	req.State = Loaded
-	return res.Segs(), req, err
+	return res.SegMetas(), err
 }
 
-func (r *DefaultResolver) needsFetching(ctx context.Context, req Request) (bool, error) {
-	nq, err := r.DB.GetNextQuery(ctx, req.Src, req.Dst, nil)
-	return time.Now().After(nq), err
-}
-
-func (r *DefaultResolver) expandCores(segs Segments, req RequestSet) ([]Request, error) {
-	// Depending on the given request and the given segments we can determine
-	// the shape of the request. If there are multiple core requests this must
-	// be a core only request and therefore no expansion needs to be done. If
-	// there are up segments but no down segments it means the request is
-	// non-core to core, because the initial request could never have had any
-	// down segments otherwise they would have been resolved by now. Similarly
-	// if we have down segments but no up segments it means the request is core
-	// to non-core. Finally if we have both up and down segments it is a
-	// non-core to non-core request. Note that "core" and "non-core" does not
-	// indicate anything about the location of the local AS. For example after
-	// resolving up segments it could be that the resolver receives a request
-	// core to non-core eventhough it is a non-core AS.
-	switch {
-	case len(req.Cores) > 1:
-		// If the request already has multiple cores there is nothing to expand.
-		return req.Cores, nil
-	case len(segs.Up) == 0 && len(segs.Down) == 0:
-		// If there is no up/down segments we can't expand anything.
-		return req.Cores, nil
-	case len(segs.Up) > 0 && len(segs.Down) == 0:
-		// Non-core to core
-		return r.expandNonCoreToCore(segs, req)
-	case len(segs.Up) == 0 && len(segs.Down) > 0:
-		// Core to non-core
-		return r.expandCoreToNonCore(segs, req)
-	default:
-		// Non-core to non-core
-		return r.expandNonCoreToNonCore(segs, req)
-	}
-}
-
-// expandNonCoreToCore expands core segments for the non-core to core case.
-func (r *DefaultResolver) expandNonCoreToCore(segs Segments,
-	req RequestSet) ([]Request, error) {
-
-	coreReq := req.Cores[0]
-	if !coreReq.Src.IsWildcard() {
-		// already resolved
-		return req.Cores, nil
-	}
-	if req.Fetch && coreReq.State == Unresolved {
-		coreReq.State = Fetch
-	}
-	upIAs := segs.Up.FirstIAs()
-	coreReqs := make([]Request, 0, len(upIAs))
-	for _, upIA := range upIAs {
-		if !upIA.Equal(coreReq.Dst) {
-			coreReqs = append(coreReqs, Request{State: coreReq.State, Src: upIA, Dst: coreReq.Dst})
-		}
-	}
-	return coreReqs, nil
-}
-
-// expandCoreToNonCore expands core segments for the core to non-core case.
-func (r *DefaultResolver) expandCoreToNonCore(segs Segments,
-	req RequestSet) ([]Request, error) {
-
-	coreReq := req.Cores[0]
-	if !coreReq.Dst.IsWildcard() {
-		// already resolved
-		return req.Cores, nil
-	}
-	if req.Fetch && coreReq.State == Unresolved {
-		coreReq.State = Fetch
-	}
-	downIAs := segs.Down.FirstIAs()
-	coreReqs := make([]Request, 0, len(downIAs))
-	for _, downIA := range downIAs {
-		if !downIA.Equal(coreReq.Src) {
-			coreReqs = append(coreReqs,
-				Request{State: coreReq.State, Src: coreReq.Src, Dst: downIA})
-		}
-	}
-	return coreReqs, nil
-}
-
-// expandNonCoreToNonCore expands core segments for the non-core to non-core case.
-func (r *DefaultResolver) expandNonCoreToNonCore(segs Segments,
-	req RequestSet) ([]Request, error) {
-
-	coreReq := req.Cores[0]
-	if !coreReq.Src.IsWildcard() && !coreReq.Dst.IsWildcard() {
-		// already resolved
-		return req.Cores, nil
-	}
-	if !coreReq.Src.IsWildcard() || !coreReq.Dst.IsWildcard() {
-		return nil, serrors.WithCtx(ErrInvalidRequest,
-			"req", req, "reason", "Core either src & dst should be wildcard or none.")
-	}
-	if req.Fetch && coreReq.State == Unresolved {
-		coreReq.State = Fetch
-	}
-	upIAs := segs.Up.FirstIAs()
-	downIAs := segs.Down.FirstIAs()
-	var coreReqs []Request
-	for _, upIA := range upIAs {
-		for _, downIA := range downIAs {
-			if !upIA.Equal(downIA) {
-				coreReqs = append(coreReqs, Request{State: coreReq.State, Src: upIA, Dst: downIA})
-			}
-		}
+func (r *DefaultResolver) loadSegment(ctx context.Context, req Request) (query.Results, error) {
+	start, end := req.Src, req.Dst
+	consDir := (req.SegType == proto.PathSegType_down)
+	if !consDir {
+		start, end = end, start
 	}
-	return coreReqs, nil
+	return r.DB.Get(ctx, &query.Params{
+		StartsAt: []addr.IA{start},
+		EndsAt:   []addr.IA{end},
+		SegTypes: []proto.PathSegType{req.SegType},
+	})
 }
 
-// resolveCores returns cores requests classified with a state.
-func (r *DefaultResolver) resolveCores(ctx context.Context,
-	req RequestSet) (Requests, error) {
-
-	needsFetching := make(map[Request]bool)
-	for i, coreReq := range req.Cores {
-		switch coreReq.State {
-		case Fetch:
-			continue
-		case Fetched:
-			req.Cores[i].State = Cached
-			continue
-		case Loaded:
-			continue
-		default:
-		}
-		coreFetch, ok := needsFetching[coreReq]
-		if !ok {
-			var err error
-			if coreFetch, err = r.needsFetching(ctx, coreReq); err != nil {
-				return req.Cores, err
-			}
-			needsFetching[coreReq] = coreFetch
-		}
-		if coreFetch {
-			req.Cores[i].State = Fetch
-		} else {
-			req.Cores[i].State = Cached
-		}
-	}
-	return req.Cores, nil
+func (r *DefaultResolver) needsFetching(ctx context.Context, req Request) (bool, error) {
+	nq, err := r.DB.GetNextQuery(ctx, req.Src, req.Dst, nil)
+	return time.Now().After(nq), err
 }
 
-func (r *DefaultResolver) allRevoked(ctx context.Context,
-	results query.Results) (bool, error) {
-
+func (r *DefaultResolver) allRevoked(ctx context.Context, results query.Results) (bool, error) {
 	segs := results.Segs()
 	filtered, err := segs.FilterSegsErr(func(ps *seg.PathSegment) (bool, error) {
 		return revcache.NoRevokedHopIntf(ctx, r.RevCache, ps)
@@ -354,8 +143,3 @@ func (r *DefaultResolver) allRevoked(ctx context.Context,
 	}
 	return len(segs) == 0 && filtered > 0, nil
 }
-
-func zeroUpDownSegsCached(r RequestSet, segs Segments) bool {
-	return (!r.Up.IsZero() && r.Up.State == Loaded && len(segs.Up) == 0) ||
-		(!r.Down.IsZero() && r.Down.State == Loaded && len(segs.Down) == 0)
-}
diff --git a/go/lib/infra/modules/segfetcher/segs.go b/go/lib/infra/modules/segfetcher/segs.go
index 89e5ff4d1..98fdb101f 100644
--- a/go/lib/infra/modules/segfetcher/segs.go
+++ b/go/lib/infra/modules/segfetcher/segs.go
@@ -16,16 +16,12 @@ package segfetcher
 
 import "github.com/scionproto/scion/go/lib/ctrl/seg"
 
-// Segments is a set of up, core, and down segments.
-type Segments struct {
-	Up   seg.Segments
-	Core seg.Segments
-	Down seg.Segments
-}
+type Segments []*seg.Meta
 
-// Add adds the other segments to the current segments.
-func (s *Segments) Add(other Segments) {
-	s.Up = append(s.Up, other.Up...)
-	s.Core = append(s.Core, other.Core...)
-	s.Down = append(s.Down, other.Down...)
+func (s Segments) Segs() []*seg.PathSegment {
+	segs := make([]*seg.PathSegment, 0, len(s))
+	for _, seg := range s {
+		segs = append(segs, seg.Segment)
+	}
+	return segs
 }
diff --git a/go/lib/infra/modules/segfetcher/splitter.go b/go/lib/infra/modules/segfetcher/splitter.go
index 791434bb1..1027b64cf 100644
--- a/go/lib/infra/modules/segfetcher/splitter.go
+++ b/go/lib/infra/modules/segfetcher/splitter.go
@@ -19,68 +19,58 @@ import (
 
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/proto"
 )
 
-// Splitter splits a single request into a request set.
+// Splitter splits a path request into set of segment requests.
 type Splitter interface {
-	// Split splits the request into a request set. Assumes that the request
-	// has been validated for the local IA.
-	Split(ctx context.Context, r Request) (RequestSet, error)
+	// Split splits a path request from the local AS to dst into a set of segment requests.
+	Split(ctx context.Context, dst addr.IA) (Requests, error)
 }
 
 // MultiSegmentSplitter splits requests consisting of one or multiple segments.
 // The AS inspector is used to check whether an IA is core or not.
 type MultiSegmentSplitter struct {
-	Local     addr.IA
+	LocalIA   addr.IA
+	Core      bool
 	Inspector trust.Inspector
 }
 
-// Split splits the request consisting of one or multiple segments.
-func (s *MultiSegmentSplitter) Split(ctx context.Context,
-	r Request) (RequestSet, error) {
+// Split splits a path request from the local AS to dst into a set of segment requests.
+func (s *MultiSegmentSplitter) Split(ctx context.Context, dst addr.IA) (Requests, error) {
 
-	if r.Src.IsZero() {
-		r.Src = s.Local
-	}
-	srcCore, err := s.isCore(ctx, r.Src)
-	if err != nil {
-		return RequestSet{}, err
-	}
-	dstCore, err := s.isCore(ctx, r.Dst)
+	const Up = proto.PathSegType_up
+	const Down = proto.PathSegType_down
+	const Core = proto.PathSegType_core
+
+	src := s.LocalIA
+	srcCore := s.Core
+	dstCore, err := s.isCore(ctx, dst)
 	if err != nil {
-		return RequestSet{}, err
+		return Requests{}, err
 	}
 	switch {
 	case !srcCore && !dstCore:
-		return RequestSet{
-			Up:    Request{Src: r.Src, Dst: toWildCard(r.Src)},
-			Cores: []Request{{Src: toWildCard(r.Src), Dst: toWildCard(r.Dst)}},
-			Down:  Request{Src: toWildCard(r.Dst), Dst: r.Dst},
-			Fetch: r.State == Fetch,
+		return Requests{
+			{Src: src, Dst: toWildCard(src), SegType: Up},
+			{Src: toWildCard(src), Dst: toWildCard(dst), SegType: Core},
+			{Src: toWildCard(dst), Dst: dst, SegType: Down},
 		}, nil
 	case !srcCore && dstCore:
-		if s.isISDLocal(r.Dst) && r.Dst.IsWildcard() {
-			return RequestSet{
-				Up:    r,
-				Fetch: r.State == Fetch,
-			}, nil
+		if src.I == dst.I && dst.IsWildcard() {
+			return Requests{{Src: src, Dst: dst, SegType: Up}}, nil
 		}
-		return RequestSet{
-			Up:    Request{Src: r.Src, Dst: toWildCard(r.Src)},
-			Cores: []Request{{Src: toWildCard(r.Src), Dst: r.Dst}},
-			Fetch: r.State == Fetch,
+		return Requests{
+			{Src: src, Dst: toWildCard(src), SegType: Up},
+			{Src: toWildCard(src), Dst: dst, SegType: Core},
 		}, nil
 	case srcCore && !dstCore:
-		return RequestSet{
-			Cores: []Request{{Src: r.Src, Dst: toWildCard(r.Dst)}},
-			Down:  Request{Src: toWildCard(r.Dst), Dst: r.Dst},
-			Fetch: r.State == Fetch,
+		return Requests{
+			{Src: src, Dst: toWildCard(dst), SegType: Core},
+			{Src: toWildCard(dst), Dst: dst, SegType: Down},
 		}, nil
 	default:
-		return RequestSet{
-			Cores: []Request{r},
-			Fetch: r.State == Fetch,
-		}, nil
+		return Requests{{Src: src, Dst: dst, SegType: Core}}, nil
 	}
 }
 
@@ -95,10 +85,6 @@ func (s *MultiSegmentSplitter) isCore(ctx context.Context, dst addr.IA) (bool, e
 	return isCore, nil
 }
 
-func (s *MultiSegmentSplitter) isISDLocal(dst addr.IA) bool {
-	return s.Local.I == dst.I
-}
-
 func toWildCard(ia addr.IA) addr.IA {
 	return addr.IA{I: ia.I}
 }
diff --git a/go/lib/pathdb/query/query.go b/go/lib/pathdb/query/query.go
index 5b538d294..c8913a2e1 100644
--- a/go/lib/pathdb/query/query.go
+++ b/go/lib/pathdb/query/query.go
@@ -79,6 +79,16 @@ func (r Results) Segs() seg.Segments {
 	return segs
 }
 
+// SegMetas returns the segments in a seg.Meta slice, i.e. returns the segments
+// with their PathSegTypes.
+func (r Results) SegMetas() []*seg.Meta {
+	segs := make([]*seg.Meta, len(r))
+	for i, r := range r {
+		segs[i] = seg.NewMeta(r.Seg, r.Type)
+	}
+	return segs
+}
+
 // ByLastUpdate implements the sort.Interface to sort results by LastUpdate time stamp.
 type ByLastUpdate Results
 
diff --git a/go/lib/snet/addrutil/addrutil.go b/go/lib/snet/addrutil/addrutil.go
index dad65c253..8b1571755 100644
--- a/go/lib/snet/addrutil/addrutil.go
+++ b/go/lib/snet/addrutil/addrutil.go
@@ -30,13 +30,32 @@ import (
 	"github.com/scionproto/scion/go/lib/topology"
 )
 
-type Pather struct {
+// Pather computes the remote address with a path based on the provided segment.
+type Pather interface {
+	GetPath(svc addr.HostSVC, ps *seg.PathSegment) (*snet.SVCAddr, error)
+}
+
+// NewPather is a temporary helper until header v2 is complete.
+func NewPather(provider topology.Provider, headerV2 bool) Pather {
+	var pather Pather = LegacyPather{TopoProvider: provider}
+	if headerV2 {
+		pather = PatherV2{
+			UnderlayNextHop: func(ifID uint16) (*net.UDPAddr, bool) {
+				return provider.Get().UnderlayNextHop2(common.IFIDType(ifID))
+			},
+		}
+	}
+	return pather
+}
+
+// PatherV2 creates paths in the V2 header format
+type PatherV2 struct {
 	// UnderlayNextHop determines the next hop underlay address for the
 	// specified interface id.
 	UnderlayNextHop func(ifID uint16) (*net.UDPAddr, bool)
 }
 
-func (p Pather) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error) {
+func (p PatherV2) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (*snet.SVCAddr, error) {
 	if len(ps.ASEntries) == 0 {
 		return nil, serrors.New("empty path")
 	}
@@ -93,16 +112,19 @@ func (p Pather) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error)
 
 }
 
+// LegacyPather creates paths in the legacy V1 header format
 type LegacyPather struct {
 	TopoProvider topology.Provider
 }
 
-func (p LegacyPather) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, error) {
+func (p LegacyPather) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (*snet.SVCAddr, error) {
 	return getPath(svc, ps, p.TopoProvider)
 }
 
-// GetPath creates a path from the given segment and then creates a snet.SVCAddr.
-func getPath(svc addr.HostSVC, ps *seg.PathSegment, topoProv topology.Provider) (net.Addr, error) {
+// getPath creates a path from the given segment and then creates a snet.SVCAddr.
+func getPath(svc addr.HostSVC, ps *seg.PathSegment,
+	topoProv topology.Provider) (*snet.SVCAddr, error) {
+
 	p, err := legacyPath(ps)
 	if err != nil {
 		return nil, serrors.WrapStr("constructing path from segment", err)
diff --git a/go/pkg/cs/BUILD.bazel b/go/pkg/cs/BUILD.bazel
index 526bac70f..dd2f8f7a7 100644
--- a/go/pkg/cs/BUILD.bazel
+++ b/go/pkg/cs/BUILD.bazel
@@ -20,13 +20,8 @@ go_library(
         "//go/cs/config:go_default_library",
         "//go/cs/ifstate:go_default_library",
         "//go/cs/keepalive:go_default_library",
-        "//go/cs/metrics:go_default_library",
         "//go/cs/onehop:go_default_library",
-        "//go/cs/segreq:go_default_library",
-        "//go/cs/segsyncer:go_default_library",
-        "//go/cs/segutil:go_default_library",
         "//go/lib/addr:go_default_library",
-        "//go/lib/common:go_default_library",
         "//go/lib/ctrl:go_default_library",
         "//go/lib/env:go_default_library",
         "//go/lib/infra:go_default_library",
@@ -34,7 +29,6 @@ go_library(
         "//go/lib/infra/messenger:go_default_library",
         "//go/lib/infra/messenger/tcp:go_default_library",
         "//go/lib/infra/modules/itopo:go_default_library",
-        "//go/lib/infra/modules/segfetcher:go_default_library",
         "//go/lib/infra/modules/seghandler:go_default_library",
         "//go/lib/keyconf:go_default_library",
         "//go/lib/log:go_default_library",
@@ -52,7 +46,6 @@ go_library(
         "//go/lib/topology:go_default_library",
         "//go/pkg/cs/trust:go_default_library",
         "//go/pkg/trust:go_default_library",
-        "//go/pkg/trust/compat:go_default_library",
         "//go/pkg/trust/renewal:go_default_library",
         "//go/proto:go_default_library",
         "@com_github_opentracing_opentracing_go//:go_default_library",
diff --git a/go/pkg/cs/messaging.go b/go/pkg/cs/messaging.go
index 0ad5e53c1..4ba5a3366 100644
--- a/go/pkg/cs/messaging.go
+++ b/go/pkg/cs/messaging.go
@@ -20,9 +20,7 @@ import (
 	"net"
 	"path/filepath"
 
-	"github.com/scionproto/scion/go/cs/segreq"
 	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/infraenv"
 	"github.com/scionproto/scion/go/lib/infra/messenger/tcp"
@@ -30,10 +28,8 @@ import (
 	"github.com/scionproto/scion/go/lib/scrypto"
 	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/snet"
-	"github.com/scionproto/scion/go/lib/snet/addrutil"
 	"github.com/scionproto/scion/go/lib/sock/reliable"
 	"github.com/scionproto/scion/go/lib/sock/reliable/reconnect"
-	"github.com/scionproto/scion/go/lib/topology"
 )
 
 // NewMessenger constructs a infra and TCP messenger based on the network
@@ -51,19 +47,6 @@ func NewMessenger(nc infraenv.NetworkConfig) (infra.Messenger, *tcp.Messenger, e
 	return msgr, tcpMsgr, nil
 }
 
-// NewPather is a temporary helper until header v2 is complete.
-func NewPather(provider topology.Provider, headerV2 bool) segreq.Pather {
-	var pather segreq.Pather = addrutil.LegacyPather{TopoProvider: provider}
-	if headerV2 {
-		pather = addrutil.Pather{
-			UnderlayNextHop: func(ifID uint16) (*net.UDPAddr, bool) {
-				return provider.Get().UnderlayNextHop2(common.IFIDType(ifID))
-			},
-		}
-	}
-	return pather
-}
-
 // MACGenFactory creates a MAC factory
 func MACGenFactory(configDir string) (func() hash.Hash, error) {
 	mk, err := keyconf.LoadMaster(filepath.Join(configDir, "keys"))
diff --git a/go/pkg/cs/tasks.go b/go/pkg/cs/tasks.go
index 35ad7a5af..c2d60bcbe 100644
--- a/go/pkg/cs/tasks.go
+++ b/go/pkg/cs/tasks.go
@@ -27,18 +27,16 @@ import (
 	"github.com/scionproto/scion/go/cs/ifstate"
 	"github.com/scionproto/scion/go/cs/keepalive"
 	"github.com/scionproto/scion/go/cs/onehop"
-	"github.com/scionproto/scion/go/cs/segsyncer"
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/ctrl"
 	"github.com/scionproto/scion/go/lib/infra"
 	"github.com/scionproto/scion/go/lib/infra/messenger"
-	"github.com/scionproto/scion/go/lib/infra/modules/itopo"
 	"github.com/scionproto/scion/go/lib/infra/modules/seghandler"
 	"github.com/scionproto/scion/go/lib/pathdb"
 	"github.com/scionproto/scion/go/lib/periodic"
 	"github.com/scionproto/scion/go/lib/revcache"
-	"github.com/scionproto/scion/go/lib/serrors"
 	"github.com/scionproto/scion/go/lib/snet"
+	"github.com/scionproto/scion/go/lib/snet/addrutil"
 	"github.com/scionproto/scion/go/lib/spath"
 	"github.com/scionproto/scion/go/lib/topology"
 	"github.com/scionproto/scion/go/pkg/trust"
@@ -72,22 +70,6 @@ type TasksConfig struct {
 	HeaderV2     bool
 }
 
-// SegSyncers creates a segment syncer tasks for every other core AS in the
-// local ISD if the local AS is a core AS.
-func (t *TasksConfig) SegSyncers() ([]*periodic.Runner, error) {
-	if !itopo.Get().Core() {
-		return nil, nil
-	}
-	return segsyncer.StartAll(segsyncer.Config{
-		IA:        itopo.Get().IA(),
-		Inspector: t.Inspector,
-		PathDB:    t.PathDB,
-		RevCache:  t.RevCache,
-		Msgr:      t.Msgr,
-		Pather:    NewPather(t.TopoProvider, t.HeaderV2),
-	})
-}
-
 // Originator starts a periodic beacon origination task. For non-core ASes, no
 // periodic runner is started.
 func (t *TasksConfig) Originator() *periodic.Runner {
@@ -162,7 +144,6 @@ func (t *TasksConfig) Registrars() []*periodic.Runner {
 func (t *TasksConfig) registrar(topo topology.Topology, segType proto.PathSegType,
 	policyType beacon.PolicyType) *periodic.Runner {
 
-	pather := NewPather(t.TopoProvider, t.HeaderV2)
 	r := &beaconing.Registrar{
 		Extender: t.extender("registrar", topo.IA(), topo.MTU(), func() spath.ExpTimeType {
 			return t.BeaconStore.MaxExpTime(policyType)
@@ -174,7 +155,7 @@ func (t *TasksConfig) registrar(topo topology.Topology, segType proto.PathSegTyp
 		Signer:   t.Signer,
 		Intfs:    t.Intfs,
 		Type:     segType,
-		Pather:   pather,
+		Pather:   addrutil.NewPather(t.TopoProvider, t.HeaderV2),
 		Tick:     beaconing.NewTick(t.RegistrationInterval),
 	}
 	return periodic.Start(r, 500*time.Millisecond, t.RegistrationInterval)
@@ -213,25 +194,17 @@ type Tasks struct {
 	Propagator *periodic.Runner
 	Registrars []*periodic.Runner
 
-	SegSyncers []*periodic.Runner
-
 	BeaconCleaner *periodic.Runner
 	PathCleaner   *periodic.Runner
 }
 
 func StartTasks(cfg TasksConfig) (*Tasks, error) {
-	segSyncers, err := cfg.SegSyncers()
-	if err != nil {
-		return nil, serrors.WrapStr("starting segment syncers", err)
-	}
-
 	beaconCleaner := beaconstorage.NewBeaconCleaner(cfg.BeaconStore)
 	revCleaner := beaconstorage.NewRevocationCleaner(cfg.BeaconStore)
 
 	segCleaner := pathdb.NewCleaner(cfg.PathDB, "ps_segments")
 	segRevCleaner := revcache.NewCleaner(cfg.RevCache, "ps_revocation")
 	return &Tasks{
-		SegSyncers: segSyncers,
 		Originator: cfg.Originator(),
 		Propagator: cfg.Propagator(),
 		Registrars: cfg.Registrars(),
@@ -271,13 +244,11 @@ func (t *Tasks) Kill() {
 		t.BeaconCleaner,
 		t.PathCleaner,
 	})
-	killRunners(t.SegSyncers)
 	killRunners(t.Registrars)
 	t.Originator = nil
 	t.Propagator = nil
 	t.BeaconCleaner = nil
 	t.PathCleaner = nil
-	t.SegSyncers = nil
 	t.Registrars = nil
 }
 
diff --git a/go/pkg/cs/trust.go b/go/pkg/cs/trust.go
index be0d238ee..4fee47465 100644
--- a/go/pkg/cs/trust.go
+++ b/go/pkg/cs/trust.go
@@ -19,19 +19,12 @@ import (
 	"path/filepath"
 	"time"
 
-	"github.com/scionproto/scion/go/cs/metrics"
-	"github.com/scionproto/scion/go/cs/segreq"
-	"github.com/scionproto/scion/go/cs/segutil"
 	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/log"
-	"github.com/scionproto/scion/go/lib/pathdb"
-	"github.com/scionproto/scion/go/lib/revcache"
 	"github.com/scionproto/scion/go/lib/serrors"
-	"github.com/scionproto/scion/go/lib/topology"
+	"github.com/scionproto/scion/go/lib/snet"
 	cstrust "github.com/scionproto/scion/go/pkg/cs/trust"
 	"github.com/scionproto/scion/go/pkg/trust"
-	"github.com/scionproto/scion/go/pkg/trust/compat"
 	"github.com/scionproto/scion/go/pkg/trust/renewal"
 )
 
@@ -61,33 +54,13 @@ func LoadTrustMaterial(configDir string, db trust.DB, logger log.Logger) error {
 // TrustProviderConfig configures how the trust provider is created.
 type TrustProviderConfig struct {
 	IA       addr.IA
-	PathDB   pathdb.PathDB
-	RevCache revcache.RevCache
 	TrustDB  trust.DB
-	RPC      interface {
-		trust.RPC
-		segfetcher.RequestAPI
-	}
-	Pather        segreq.Pather
-	Inspector     trust.Inspector
-	Provider      topology.Provider
-	DstProvider   segfetcher.DstProvider
-	QueryInterval time.Duration
-	Core          bool
-	HeaderV2      bool
+	RPC      trust.RPC
+	HeaderV2 bool
 }
 
 // NewTrustProvider creates a trust material provider that uses paths from the path storage.
 func NewTrustProvider(cfg TrustProviderConfig) trust.FetchingProvider {
-	trustRouter := &segutil.Router{
-		Pather: segfetcher.Pather{
-			PathDB:       cfg.PathDB,
-			RevCache:     cfg.RevCache,
-			TopoProvider: cfg.Provider,
-			HeaderV2:     cfg.HeaderV2,
-			// Fetcher needs to be initialized with a provider.
-		},
-	}
 	provider := trust.FetchingProvider{
 		DB: cfg.TrustDB,
 		Fetcher: trust.DefaultFetcher{
@@ -95,30 +68,21 @@ func NewTrustProvider(cfg TrustProviderConfig) trust.FetchingProvider {
 			IA:  cfg.IA,
 		},
 		Recurser: trust.ASLocalRecurser{IA: cfg.IA},
-		Router: trust.AuthRouter{
-			ISD:    cfg.IA.I,
-			DB:     cfg.TrustDB,
-			Router: trustRouter,
-		},
 	}
-	trustRouter.Pather.Fetcher = segfetcher.FetcherConfig{
-		QueryInterval: cfg.QueryInterval,
-		LocalIA:       cfg.IA,
-		Verifier:      compat.Verifier{Verifier: trust.Verifier{Engine: provider}},
-		PathDB:        cfg.PathDB,
-		RevCache:      cfg.RevCache,
-		RequestAPI:    cfg.RPC,
-		DstProvider:   cfg.DstProvider,
-		Splitter: &segfetcher.MultiSegmentSplitter{
-			Local:     cfg.IA,
-			Inspector: cfg.Inspector,
-		},
-		MetricsNamespace: metrics.PSNamespace,
-		LocalInfo:        segreq.CreateLocalInfo(cfg.Core, cfg.IA, cfg.Inspector),
-	}.New()
 	return provider
 }
 
+// SetTrustRouter initializes the Router of the FetchingProvider.
+// This is separate from NewTrustProvider due to the mutual dependence of
+// the provider and the router.
+func SetTrustRouter(provider *trust.FetchingProvider, router snet.Router) {
+	provider.Router = trust.AuthRouter{
+		ISD:    provider.Fetcher.(trust.DefaultFetcher).IA.I,
+		DB:     provider.DB,
+		Router: router,
+	}
+}
+
 // NewSigner creates a renewing signer backed by a certificate chain..
 func NewSigner(ia addr.IA, db trust.DB, cfgDir string) (cstrust.RenewingSigner, error) {
 	ctx, cancelF := context.WithTimeout(context.Background(), time.Second)
diff --git a/go/pkg/sciond/fetcher/fetcher.go b/go/pkg/sciond/fetcher/fetcher.go
index 0aa2948a7..fb029f0e6 100644
--- a/go/pkg/sciond/fetcher/fetcher.go
+++ b/go/pkg/sciond/fetcher/fetcher.go
@@ -66,24 +66,23 @@ func NewFetcher(requestAPI segfetcher.RequestAPI, pathDB pathdb.PathDB, inspecto
 	localIA := topoProvider.Get().IA()
 	return &fetcher{
 		pather: segfetcher.Pather{
-			PathDB:       pathDB,
 			RevCache:     revCache,
 			TopoProvider: topoProvider,
 			Fetcher: segfetcher.FetcherConfig{
-				QueryInterval: cfg.QueryInterval.Duration,
-				LocalIA:       localIA,
-				Verifier:      verifier,
-				PathDB:        pathDB,
-				RevCache:      revCache,
-				RequestAPI:    requestAPI,
-				DstProvider:   &dstProvider{TopologyProvider: topoProvider},
-				Splitter: &segfetcher.MultiSegmentSplitter{
-					Local:     localIA,
-					Inspector: inspector,
-				},
+				QueryInterval:    cfg.QueryInterval.Duration,
+				Verifier:         verifier,
+				PathDB:           pathDB,
+				RevCache:         revCache,
+				RequestAPI:       requestAPI,
+				DstProvider:      &dstProvider{TopologyProvider: topoProvider},
 				MetricsNamespace: metrics.Namespace,
 				LocalInfo:        neverLocal{},
 			}.New(),
+			Splitter: &segfetcher.MultiSegmentSplitter{
+				LocalIA:   localIA,
+				Core:      topoProvider.Get().Core(),
+				Inspector: inspector,
+			},
 			HeaderV2: headerV2,
 		},
 		config: cfg,
@@ -196,4 +195,6 @@ func (r *dstProvider) Dst(_ context.Context, _ segfetcher.Request) (net.Addr, er
 
 type neverLocal struct{}
 
-func (neverLocal) IsSegLocal(_ context.Context, _, _ addr.IA) (bool, error) { return false, nil }
+func (neverLocal) IsSegLocal(_ segfetcher.Request) bool {
+	return false
+}
diff --git a/go/proto/path_mgmt.capnp.go b/go/proto/path_mgmt.capnp.go
index 15446c469..7b1600ad3 100644
--- a/go/proto/path_mgmt.capnp.go
+++ b/go/proto/path_mgmt.capnp.go
@@ -315,67 +315,6 @@ func (p SegReply_Promise) Recs() SegRecs_Promise {
 	return SegRecs_Promise{Pipeline: p.Pipeline.GetPipeline(1)}
 }
 
-type SegChangesIdReq struct{ capnp.Struct }
-
-// SegChangesIdReq_TypeID is the unique identifier for the type SegChangesIdReq.
-const SegChangesIdReq_TypeID = 0xc88dfa6be7a1d091
-
-func NewSegChangesIdReq(s *capnp.Segment) (SegChangesIdReq, error) {
-	st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 0})
-	return SegChangesIdReq{st}, err
-}
-
-func NewRootSegChangesIdReq(s *capnp.Segment) (SegChangesIdReq, error) {
-	st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 8, PointerCount: 0})
-	return SegChangesIdReq{st}, err
-}
-
-func ReadRootSegChangesIdReq(msg *capnp.Message) (SegChangesIdReq, error) {
-	root, err := msg.RootPtr()
-	return SegChangesIdReq{root.Struct()}, err
-}
-
-func (s SegChangesIdReq) String() string {
-	str, _ := text.Marshal(0xc88dfa6be7a1d091, s.Struct)
-	return str
-}
-
-func (s SegChangesIdReq) LastCheck() uint32 {
-	return s.Struct.Uint32(0)
-}
-
-func (s SegChangesIdReq) SetLastCheck(v uint32) {
-	s.Struct.SetUint32(0, v)
-}
-
-// SegChangesIdReq_List is a list of SegChangesIdReq.
-type SegChangesIdReq_List struct{ capnp.List }
-
-// NewSegChangesIdReq creates a new list of SegChangesIdReq.
-func NewSegChangesIdReq_List(s *capnp.Segment, sz int32) (SegChangesIdReq_List, error) {
-	l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 8, PointerCount: 0}, sz)
-	return SegChangesIdReq_List{l}, err
-}
-
-func (s SegChangesIdReq_List) At(i int) SegChangesIdReq { return SegChangesIdReq{s.List.Struct(i)} }
-
-func (s SegChangesIdReq_List) Set(i int, v SegChangesIdReq) error {
-	return s.List.SetStruct(i, v.Struct)
-}
-
-func (s SegChangesIdReq_List) String() string {
-	str, _ := text.MarshalList(0xc88dfa6be7a1d091, s.List)
-	return str
-}
-
-// SegChangesIdReq_Promise is a wrapper for a SegChangesIdReq promised by a client call.
-type SegChangesIdReq_Promise struct{ *capnp.Pipeline }
-
-func (p SegChangesIdReq_Promise) Struct() (SegChangesIdReq, error) {
-	s, err := p.Pipeline.Struct()
-	return SegChangesIdReq{s}, err
-}
-
 type SegIds struct{ capnp.Struct }
 
 // SegIds_TypeID is the unique identifier for the type SegIds.
@@ -455,162 +394,6 @@ func (p SegIds_Promise) Struct() (SegIds, error) {
 	return SegIds{s}, err
 }
 
-type SegChangesIdReply struct{ capnp.Struct }
-
-// SegChangesIdReply_TypeID is the unique identifier for the type SegChangesIdReply.
-const SegChangesIdReply_TypeID = 0xbd56ceeaf8c65140
-
-func NewSegChangesIdReply(s *capnp.Segment) (SegChangesIdReply, error) {
-	st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1})
-	return SegChangesIdReply{st}, err
-}
-
-func NewRootSegChangesIdReply(s *capnp.Segment) (SegChangesIdReply, error) {
-	st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1})
-	return SegChangesIdReply{st}, err
-}
-
-func ReadRootSegChangesIdReply(msg *capnp.Message) (SegChangesIdReply, error) {
-	root, err := msg.RootPtr()
-	return SegChangesIdReply{root.Struct()}, err
-}
-
-func (s SegChangesIdReply) String() string {
-	str, _ := text.Marshal(0xbd56ceeaf8c65140, s.Struct)
-	return str
-}
-
-func (s SegChangesIdReply) Ids() (SegIds_List, error) {
-	p, err := s.Struct.Ptr(0)
-	return SegIds_List{List: p.List()}, err
-}
-
-func (s SegChangesIdReply) HasIds() bool {
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s SegChangesIdReply) SetIds(v SegIds_List) error {
-	return s.Struct.SetPtr(0, v.List.ToPtr())
-}
-
-// NewIds sets the ids field to a newly
-// allocated SegIds_List, preferring placement in s's segment.
-func (s SegChangesIdReply) NewIds(n int32) (SegIds_List, error) {
-	l, err := NewSegIds_List(s.Struct.Segment(), n)
-	if err != nil {
-		return SegIds_List{}, err
-	}
-	err = s.Struct.SetPtr(0, l.List.ToPtr())
-	return l, err
-}
-
-// SegChangesIdReply_List is a list of SegChangesIdReply.
-type SegChangesIdReply_List struct{ capnp.List }
-
-// NewSegChangesIdReply creates a new list of SegChangesIdReply.
-func NewSegChangesIdReply_List(s *capnp.Segment, sz int32) (SegChangesIdReply_List, error) {
-	l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz)
-	return SegChangesIdReply_List{l}, err
-}
-
-func (s SegChangesIdReply_List) At(i int) SegChangesIdReply {
-	return SegChangesIdReply{s.List.Struct(i)}
-}
-
-func (s SegChangesIdReply_List) Set(i int, v SegChangesIdReply) error {
-	return s.List.SetStruct(i, v.Struct)
-}
-
-func (s SegChangesIdReply_List) String() string {
-	str, _ := text.MarshalList(0xbd56ceeaf8c65140, s.List)
-	return str
-}
-
-// SegChangesIdReply_Promise is a wrapper for a SegChangesIdReply promised by a client call.
-type SegChangesIdReply_Promise struct{ *capnp.Pipeline }
-
-func (p SegChangesIdReply_Promise) Struct() (SegChangesIdReply, error) {
-	s, err := p.Pipeline.Struct()
-	return SegChangesIdReply{s}, err
-}
-
-type SegChangesReq struct{ capnp.Struct }
-
-// SegChangesReq_TypeID is the unique identifier for the type SegChangesReq.
-const SegChangesReq_TypeID = 0xa7ad0c62a234c68b
-
-func NewSegChangesReq(s *capnp.Segment) (SegChangesReq, error) {
-	st, err := capnp.NewStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1})
-	return SegChangesReq{st}, err
-}
-
-func NewRootSegChangesReq(s *capnp.Segment) (SegChangesReq, error) {
-	st, err := capnp.NewRootStruct(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1})
-	return SegChangesReq{st}, err
-}
-
-func ReadRootSegChangesReq(msg *capnp.Message) (SegChangesReq, error) {
-	root, err := msg.RootPtr()
-	return SegChangesReq{root.Struct()}, err
-}
-
-func (s SegChangesReq) String() string {
-	str, _ := text.Marshal(0xa7ad0c62a234c68b, s.Struct)
-	return str
-}
-
-func (s SegChangesReq) SegIds() (capnp.DataList, error) {
-	p, err := s.Struct.Ptr(0)
-	return capnp.DataList{List: p.List()}, err
-}
-
-func (s SegChangesReq) HasSegIds() bool {
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s SegChangesReq) SetSegIds(v capnp.DataList) error {
-	return s.Struct.SetPtr(0, v.List.ToPtr())
-}
-
-// NewSegIds sets the segIds field to a newly
-// allocated capnp.DataList, preferring placement in s's segment.
-func (s SegChangesReq) NewSegIds(n int32) (capnp.DataList, error) {
-	l, err := capnp.NewDataList(s.Struct.Segment(), n)
-	if err != nil {
-		return capnp.DataList{}, err
-	}
-	err = s.Struct.SetPtr(0, l.List.ToPtr())
-	return l, err
-}
-
-// SegChangesReq_List is a list of SegChangesReq.
-type SegChangesReq_List struct{ capnp.List }
-
-// NewSegChangesReq creates a new list of SegChangesReq.
-func NewSegChangesReq_List(s *capnp.Segment, sz int32) (SegChangesReq_List, error) {
-	l, err := capnp.NewCompositeList(s, capnp.ObjectSize{DataSize: 0, PointerCount: 1}, sz)
-	return SegChangesReq_List{l}, err
-}
-
-func (s SegChangesReq_List) At(i int) SegChangesReq { return SegChangesReq{s.List.Struct(i)} }
-
-func (s SegChangesReq_List) Set(i int, v SegChangesReq) error { return s.List.SetStruct(i, v.Struct) }
-
-func (s SegChangesReq_List) String() string {
-	str, _ := text.MarshalList(0xa7ad0c62a234c68b, s.List)
-	return str
-}
-
-// SegChangesReq_Promise is a wrapper for a SegChangesReq promised by a client call.
-type SegChangesReq_Promise struct{ *capnp.Pipeline }
-
-func (p SegChangesReq_Promise) Struct() (SegChangesReq, error) {
-	s, err := p.Pipeline.Struct()
-	return SegChangesReq{s}, err
-}
-
 type HPGroupId struct{ capnp.Struct }
 
 // HPGroupId_TypeID is the unique identifier for the type HPGroupId.
@@ -1272,27 +1055,22 @@ type PathMgmt struct{ capnp.Struct }
 type PathMgmt_Which uint16
 
 const (
-	PathMgmt_Which_unset             PathMgmt_Which = 0
-	PathMgmt_Which_segReq            PathMgmt_Which = 1
-	PathMgmt_Which_segReply          PathMgmt_Which = 2
-	PathMgmt_Which_segReg            PathMgmt_Which = 3
-	PathMgmt_Which_segSync           PathMgmt_Which = 4
-	PathMgmt_Which_sRevInfo          PathMgmt_Which = 5
-	PathMgmt_Which_ifStateReq        PathMgmt_Which = 6
-	PathMgmt_Which_ifStateInfos      PathMgmt_Which = 7
-	PathMgmt_Which_segChangesIdReq   PathMgmt_Which = 8
-	PathMgmt_Which_segChangesIdReply PathMgmt_Which = 9
-	PathMgmt_Which_segChangesReq     PathMgmt_Which = 10
-	PathMgmt_Which_segChangesReply   PathMgmt_Which = 11
-	PathMgmt_Which_hpSegReq          PathMgmt_Which = 12
-	PathMgmt_Which_hpSegReply        PathMgmt_Which = 13
-	PathMgmt_Which_hpSegReg          PathMgmt_Which = 14
-	PathMgmt_Which_hpCfgReq          PathMgmt_Which = 15
-	PathMgmt_Which_hpCfgReply        PathMgmt_Which = 16
+	PathMgmt_Which_unset        PathMgmt_Which = 0
+	PathMgmt_Which_segReq       PathMgmt_Which = 1
+	PathMgmt_Which_segReply     PathMgmt_Which = 2
+	PathMgmt_Which_segReg       PathMgmt_Which = 3
+	PathMgmt_Which_sRevInfo     PathMgmt_Which = 4
+	PathMgmt_Which_ifStateReq   PathMgmt_Which = 5
+	PathMgmt_Which_ifStateInfos PathMgmt_Which = 6
+	PathMgmt_Which_hpSegReq     PathMgmt_Which = 7
+	PathMgmt_Which_hpSegReply   PathMgmt_Which = 8
+	PathMgmt_Which_hpSegReg     PathMgmt_Which = 9
+	PathMgmt_Which_hpCfgReq     PathMgmt_Which = 10
+	PathMgmt_Which_hpCfgReply   PathMgmt_Which = 11
 )
 
 func (w PathMgmt_Which) String() string {
-	const s = "unsetsegReqsegReplysegRegsegSyncsRevInfoifStateReqifStateInfossegChangesIdReqsegChangesIdReplysegChangesReqsegChangesReplyhpSegReqhpSegReplyhpSegReghpCfgReqhpCfgReply"
+	const s = "unsetsegReqsegReplysegRegsRevInfoifStateReqifStateInfoshpSegReqhpSegReplyhpSegReghpCfgReqhpCfgReply"
 	switch w {
 	case PathMgmt_Which_unset:
 		return s[0:5]
@@ -1302,32 +1080,22 @@ func (w PathMgmt_Which) String() string {
 		return s[11:19]
 	case PathMgmt_Which_segReg:
 		return s[19:25]
-	case PathMgmt_Which_segSync:
-		return s[25:32]
 	case PathMgmt_Which_sRevInfo:
-		return s[32:40]
+		return s[25:33]
 	case PathMgmt_Which_ifStateReq:
-		return s[40:50]
+		return s[33:43]
 	case PathMgmt_Which_ifStateInfos:
-		return s[50:62]
-	case PathMgmt_Which_segChangesIdReq:
-		return s[62:77]
-	case PathMgmt_Which_segChangesIdReply:
-		return s[77:94]
-	case PathMgmt_Which_segChangesReq:
-		return s[94:107]
-	case PathMgmt_Which_segChangesReply:
-		return s[107:122]
+		return s[43:55]
 	case PathMgmt_Which_hpSegReq:
-		return s[122:130]
+		return s[55:63]
 	case PathMgmt_Which_hpSegReply:
-		return s[130:140]
+		return s[63:73]
 	case PathMgmt_Which_hpSegReg:
-		return s[140:148]
+		return s[73:81]
 	case PathMgmt_Which_hpCfgReq:
-		return s[148:156]
+		return s[81:89]
 	case PathMgmt_Which_hpCfgReply:
-		return s[156:166]
+		return s[89:99]
 
 	}
 	return "PathMgmt_Which(" + strconv.FormatUint(uint64(w), 10) + ")"
@@ -1463,41 +1231,8 @@ func (s PathMgmt) NewSegReg() (SegRecs, error) {
 	return ss, err
 }
 
-func (s PathMgmt) SegSync() (SegRecs, error) {
-	if s.Struct.Uint16(0) != 4 {
-		panic("Which() != segSync")
-	}
-	p, err := s.Struct.Ptr(0)
-	return SegRecs{Struct: p.Struct()}, err
-}
-
-func (s PathMgmt) HasSegSync() bool {
-	if s.Struct.Uint16(0) != 4 {
-		return false
-	}
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s PathMgmt) SetSegSync(v SegRecs) error {
-	s.Struct.SetUint16(0, 4)
-	return s.Struct.SetPtr(0, v.Struct.ToPtr())
-}
-
-// NewSegSync sets the segSync field to a newly
-// allocated SegRecs struct, preferring placement in s's segment.
-func (s PathMgmt) NewSegSync() (SegRecs, error) {
-	s.Struct.SetUint16(0, 4)
-	ss, err := NewSegRecs(s.Struct.Segment())
-	if err != nil {
-		return SegRecs{}, err
-	}
-	err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
-	return ss, err
-}
-
 func (s PathMgmt) SRevInfo() (SignedBlob, error) {
-	if s.Struct.Uint16(0) != 5 {
+	if s.Struct.Uint16(0) != 4 {
 		panic("Which() != sRevInfo")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1505,7 +1240,7 @@ func (s PathMgmt) SRevInfo() (SignedBlob, error) {
 }
 
 func (s PathMgmt) HasSRevInfo() bool {
-	if s.Struct.Uint16(0) != 5 {
+	if s.Struct.Uint16(0) != 4 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1513,14 +1248,14 @@ func (s PathMgmt) HasSRevInfo() bool {
 }
 
 func (s PathMgmt) SetSRevInfo(v SignedBlob) error {
-	s.Struct.SetUint16(0, 5)
+	s.Struct.SetUint16(0, 4)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewSRevInfo sets the sRevInfo field to a newly
 // allocated SignedBlob struct, preferring placement in s's segment.
 func (s PathMgmt) NewSRevInfo() (SignedBlob, error) {
-	s.Struct.SetUint16(0, 5)
+	s.Struct.SetUint16(0, 4)
 	ss, err := NewSignedBlob(s.Struct.Segment())
 	if err != nil {
 		return SignedBlob{}, err
@@ -1530,7 +1265,7 @@ func (s PathMgmt) NewSRevInfo() (SignedBlob, error) {
 }
 
 func (s PathMgmt) IfStateReq() (IFStateReq, error) {
-	if s.Struct.Uint16(0) != 6 {
+	if s.Struct.Uint16(0) != 5 {
 		panic("Which() != ifStateReq")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1538,7 +1273,7 @@ func (s PathMgmt) IfStateReq() (IFStateReq, error) {
 }
 
 func (s PathMgmt) HasIfStateReq() bool {
-	if s.Struct.Uint16(0) != 6 {
+	if s.Struct.Uint16(0) != 5 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1546,14 +1281,14 @@ func (s PathMgmt) HasIfStateReq() bool {
 }
 
 func (s PathMgmt) SetIfStateReq(v IFStateReq) error {
-	s.Struct.SetUint16(0, 6)
+	s.Struct.SetUint16(0, 5)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewIfStateReq sets the ifStateReq field to a newly
 // allocated IFStateReq struct, preferring placement in s's segment.
 func (s PathMgmt) NewIfStateReq() (IFStateReq, error) {
-	s.Struct.SetUint16(0, 6)
+	s.Struct.SetUint16(0, 5)
 	ss, err := NewIFStateReq(s.Struct.Segment())
 	if err != nil {
 		return IFStateReq{}, err
@@ -1563,7 +1298,7 @@ func (s PathMgmt) NewIfStateReq() (IFStateReq, error) {
 }
 
 func (s PathMgmt) IfStateInfos() (IFStateInfos, error) {
-	if s.Struct.Uint16(0) != 7 {
+	if s.Struct.Uint16(0) != 6 {
 		panic("Which() != ifStateInfos")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1571,7 +1306,7 @@ func (s PathMgmt) IfStateInfos() (IFStateInfos, error) {
 }
 
 func (s PathMgmt) HasIfStateInfos() bool {
-	if s.Struct.Uint16(0) != 7 {
+	if s.Struct.Uint16(0) != 6 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1579,14 +1314,14 @@ func (s PathMgmt) HasIfStateInfos() bool {
 }
 
 func (s PathMgmt) SetIfStateInfos(v IFStateInfos) error {
-	s.Struct.SetUint16(0, 7)
+	s.Struct.SetUint16(0, 6)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewIfStateInfos sets the ifStateInfos field to a newly
 // allocated IFStateInfos struct, preferring placement in s's segment.
 func (s PathMgmt) NewIfStateInfos() (IFStateInfos, error) {
-	s.Struct.SetUint16(0, 7)
+	s.Struct.SetUint16(0, 6)
 	ss, err := NewIFStateInfos(s.Struct.Segment())
 	if err != nil {
 		return IFStateInfos{}, err
@@ -1595,140 +1330,8 @@ func (s PathMgmt) NewIfStateInfos() (IFStateInfos, error) {
 	return ss, err
 }
 
-func (s PathMgmt) SegChangesIdReq() (SegChangesIdReq, error) {
-	if s.Struct.Uint16(0) != 8 {
-		panic("Which() != segChangesIdReq")
-	}
-	p, err := s.Struct.Ptr(0)
-	return SegChangesIdReq{Struct: p.Struct()}, err
-}
-
-func (s PathMgmt) HasSegChangesIdReq() bool {
-	if s.Struct.Uint16(0) != 8 {
-		return false
-	}
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s PathMgmt) SetSegChangesIdReq(v SegChangesIdReq) error {
-	s.Struct.SetUint16(0, 8)
-	return s.Struct.SetPtr(0, v.Struct.ToPtr())
-}
-
-// NewSegChangesIdReq sets the segChangesIdReq field to a newly
-// allocated SegChangesIdReq struct, preferring placement in s's segment.
-func (s PathMgmt) NewSegChangesIdReq() (SegChangesIdReq, error) {
-	s.Struct.SetUint16(0, 8)
-	ss, err := NewSegChangesIdReq(s.Struct.Segment())
-	if err != nil {
-		return SegChangesIdReq{}, err
-	}
-	err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
-	return ss, err
-}
-
-func (s PathMgmt) SegChangesIdReply() (SegChangesIdReply, error) {
-	if s.Struct.Uint16(0) != 9 {
-		panic("Which() != segChangesIdReply")
-	}
-	p, err := s.Struct.Ptr(0)
-	return SegChangesIdReply{Struct: p.Struct()}, err
-}
-
-func (s PathMgmt) HasSegChangesIdReply() bool {
-	if s.Struct.Uint16(0) != 9 {
-		return false
-	}
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s PathMgmt) SetSegChangesIdReply(v SegChangesIdReply) error {
-	s.Struct.SetUint16(0, 9)
-	return s.Struct.SetPtr(0, v.Struct.ToPtr())
-}
-
-// NewSegChangesIdReply sets the segChangesIdReply field to a newly
-// allocated SegChangesIdReply struct, preferring placement in s's segment.
-func (s PathMgmt) NewSegChangesIdReply() (SegChangesIdReply, error) {
-	s.Struct.SetUint16(0, 9)
-	ss, err := NewSegChangesIdReply(s.Struct.Segment())
-	if err != nil {
-		return SegChangesIdReply{}, err
-	}
-	err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
-	return ss, err
-}
-
-func (s PathMgmt) SegChangesReq() (SegChangesReq, error) {
-	if s.Struct.Uint16(0) != 10 {
-		panic("Which() != segChangesReq")
-	}
-	p, err := s.Struct.Ptr(0)
-	return SegChangesReq{Struct: p.Struct()}, err
-}
-
-func (s PathMgmt) HasSegChangesReq() bool {
-	if s.Struct.Uint16(0) != 10 {
-		return false
-	}
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s PathMgmt) SetSegChangesReq(v SegChangesReq) error {
-	s.Struct.SetUint16(0, 10)
-	return s.Struct.SetPtr(0, v.Struct.ToPtr())
-}
-
-// NewSegChangesReq sets the segChangesReq field to a newly
-// allocated SegChangesReq struct, preferring placement in s's segment.
-func (s PathMgmt) NewSegChangesReq() (SegChangesReq, error) {
-	s.Struct.SetUint16(0, 10)
-	ss, err := NewSegChangesReq(s.Struct.Segment())
-	if err != nil {
-		return SegChangesReq{}, err
-	}
-	err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
-	return ss, err
-}
-
-func (s PathMgmt) SegChangesReply() (SegRecs, error) {
-	if s.Struct.Uint16(0) != 11 {
-		panic("Which() != segChangesReply")
-	}
-	p, err := s.Struct.Ptr(0)
-	return SegRecs{Struct: p.Struct()}, err
-}
-
-func (s PathMgmt) HasSegChangesReply() bool {
-	if s.Struct.Uint16(0) != 11 {
-		return false
-	}
-	p, err := s.Struct.Ptr(0)
-	return p.IsValid() || err != nil
-}
-
-func (s PathMgmt) SetSegChangesReply(v SegRecs) error {
-	s.Struct.SetUint16(0, 11)
-	return s.Struct.SetPtr(0, v.Struct.ToPtr())
-}
-
-// NewSegChangesReply sets the segChangesReply field to a newly
-// allocated SegRecs struct, preferring placement in s's segment.
-func (s PathMgmt) NewSegChangesReply() (SegRecs, error) {
-	s.Struct.SetUint16(0, 11)
-	ss, err := NewSegRecs(s.Struct.Segment())
-	if err != nil {
-		return SegRecs{}, err
-	}
-	err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
-	return ss, err
-}
-
 func (s PathMgmt) HpSegReq() (HPSegReq, error) {
-	if s.Struct.Uint16(0) != 12 {
+	if s.Struct.Uint16(0) != 7 {
 		panic("Which() != hpSegReq")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1736,7 +1339,7 @@ func (s PathMgmt) HpSegReq() (HPSegReq, error) {
 }
 
 func (s PathMgmt) HasHpSegReq() bool {
-	if s.Struct.Uint16(0) != 12 {
+	if s.Struct.Uint16(0) != 7 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1744,14 +1347,14 @@ func (s PathMgmt) HasHpSegReq() bool {
 }
 
 func (s PathMgmt) SetHpSegReq(v HPSegReq) error {
-	s.Struct.SetUint16(0, 12)
+	s.Struct.SetUint16(0, 7)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewHpSegReq sets the hpSegReq field to a newly
 // allocated HPSegReq struct, preferring placement in s's segment.
 func (s PathMgmt) NewHpSegReq() (HPSegReq, error) {
-	s.Struct.SetUint16(0, 12)
+	s.Struct.SetUint16(0, 7)
 	ss, err := NewHPSegReq(s.Struct.Segment())
 	if err != nil {
 		return HPSegReq{}, err
@@ -1761,7 +1364,7 @@ func (s PathMgmt) NewHpSegReq() (HPSegReq, error) {
 }
 
 func (s PathMgmt) HpSegReply() (HPSegReply, error) {
-	if s.Struct.Uint16(0) != 13 {
+	if s.Struct.Uint16(0) != 8 {
 		panic("Which() != hpSegReply")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1769,7 +1372,7 @@ func (s PathMgmt) HpSegReply() (HPSegReply, error) {
 }
 
 func (s PathMgmt) HasHpSegReply() bool {
-	if s.Struct.Uint16(0) != 13 {
+	if s.Struct.Uint16(0) != 8 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1777,14 +1380,14 @@ func (s PathMgmt) HasHpSegReply() bool {
 }
 
 func (s PathMgmt) SetHpSegReply(v HPSegReply) error {
-	s.Struct.SetUint16(0, 13)
+	s.Struct.SetUint16(0, 8)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewHpSegReply sets the hpSegReply field to a newly
 // allocated HPSegReply struct, preferring placement in s's segment.
 func (s PathMgmt) NewHpSegReply() (HPSegReply, error) {
-	s.Struct.SetUint16(0, 13)
+	s.Struct.SetUint16(0, 8)
 	ss, err := NewHPSegReply(s.Struct.Segment())
 	if err != nil {
 		return HPSegReply{}, err
@@ -1794,7 +1397,7 @@ func (s PathMgmt) NewHpSegReply() (HPSegReply, error) {
 }
 
 func (s PathMgmt) HpSegReg() (HPSegRecs, error) {
-	if s.Struct.Uint16(0) != 14 {
+	if s.Struct.Uint16(0) != 9 {
 		panic("Which() != hpSegReg")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1802,7 +1405,7 @@ func (s PathMgmt) HpSegReg() (HPSegRecs, error) {
 }
 
 func (s PathMgmt) HasHpSegReg() bool {
-	if s.Struct.Uint16(0) != 14 {
+	if s.Struct.Uint16(0) != 9 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1810,14 +1413,14 @@ func (s PathMgmt) HasHpSegReg() bool {
 }
 
 func (s PathMgmt) SetHpSegReg(v HPSegRecs) error {
-	s.Struct.SetUint16(0, 14)
+	s.Struct.SetUint16(0, 9)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewHpSegReg sets the hpSegReg field to a newly
 // allocated HPSegRecs struct, preferring placement in s's segment.
 func (s PathMgmt) NewHpSegReg() (HPSegRecs, error) {
-	s.Struct.SetUint16(0, 14)
+	s.Struct.SetUint16(0, 9)
 	ss, err := NewHPSegRecs(s.Struct.Segment())
 	if err != nil {
 		return HPSegRecs{}, err
@@ -1827,7 +1430,7 @@ func (s PathMgmt) NewHpSegReg() (HPSegRecs, error) {
 }
 
 func (s PathMgmt) HpCfgReq() (HPCfgReq, error) {
-	if s.Struct.Uint16(0) != 15 {
+	if s.Struct.Uint16(0) != 10 {
 		panic("Which() != hpCfgReq")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1835,7 +1438,7 @@ func (s PathMgmt) HpCfgReq() (HPCfgReq, error) {
 }
 
 func (s PathMgmt) HasHpCfgReq() bool {
-	if s.Struct.Uint16(0) != 15 {
+	if s.Struct.Uint16(0) != 10 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1843,14 +1446,14 @@ func (s PathMgmt) HasHpCfgReq() bool {
 }
 
 func (s PathMgmt) SetHpCfgReq(v HPCfgReq) error {
-	s.Struct.SetUint16(0, 15)
+	s.Struct.SetUint16(0, 10)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewHpCfgReq sets the hpCfgReq field to a newly
 // allocated HPCfgReq struct, preferring placement in s's segment.
 func (s PathMgmt) NewHpCfgReq() (HPCfgReq, error) {
-	s.Struct.SetUint16(0, 15)
+	s.Struct.SetUint16(0, 10)
 	ss, err := NewHPCfgReq(s.Struct.Segment())
 	if err != nil {
 		return HPCfgReq{}, err
@@ -1860,7 +1463,7 @@ func (s PathMgmt) NewHpCfgReq() (HPCfgReq, error) {
 }
 
 func (s PathMgmt) HpCfgReply() (HPCfgReply, error) {
-	if s.Struct.Uint16(0) != 16 {
+	if s.Struct.Uint16(0) != 11 {
 		panic("Which() != hpCfgReply")
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1868,7 +1471,7 @@ func (s PathMgmt) HpCfgReply() (HPCfgReply, error) {
 }
 
 func (s PathMgmt) HasHpCfgReply() bool {
-	if s.Struct.Uint16(0) != 16 {
+	if s.Struct.Uint16(0) != 11 {
 		return false
 	}
 	p, err := s.Struct.Ptr(0)
@@ -1876,14 +1479,14 @@ func (s PathMgmt) HasHpCfgReply() bool {
 }
 
 func (s PathMgmt) SetHpCfgReply(v HPCfgReply) error {
-	s.Struct.SetUint16(0, 16)
+	s.Struct.SetUint16(0, 11)
 	return s.Struct.SetPtr(0, v.Struct.ToPtr())
 }
 
 // NewHpCfgReply sets the hpCfgReply field to a newly
 // allocated HPCfgReply struct, preferring placement in s's segment.
 func (s PathMgmt) NewHpCfgReply() (HPCfgReply, error) {
-	s.Struct.SetUint16(0, 16)
+	s.Struct.SetUint16(0, 11)
 	ss, err := NewHPCfgReply(s.Struct.Segment())
 	if err != nil {
 		return HPCfgReply{}, err
@@ -1930,10 +1533,6 @@ func (p PathMgmt_Promise) SegReg() SegRecs_Promise {
 	return SegRecs_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
 }
 
-func (p PathMgmt_Promise) SegSync() SegRecs_Promise {
-	return SegRecs_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
-}
-
 func (p PathMgmt_Promise) SRevInfo() SignedBlob_Promise {
 	return SignedBlob_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
 }
@@ -1946,22 +1545,6 @@ func (p PathMgmt_Promise) IfStateInfos() IFStateInfos_Promise {
 	return IFStateInfos_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
 }
 
-func (p PathMgmt_Promise) SegChangesIdReq() SegChangesIdReq_Promise {
-	return SegChangesIdReq_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
-}
-
-func (p PathMgmt_Promise) SegChangesIdReply() SegChangesIdReply_Promise {
-	return SegChangesIdReply_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
-}
-
-func (p PathMgmt_Promise) SegChangesReq() SegChangesReq_Promise {
-	return SegChangesReq_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
-}
-
-func (p PathMgmt_Promise) SegChangesReply() SegRecs_Promise {
-	return SegRecs_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
-}
-
 func (p PathMgmt_Promise) HpSegReq() HPSegReq_Promise {
 	return HPSegReq_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
 }
@@ -1982,107 +1565,90 @@ func (p PathMgmt_Promise) HpCfgReply() HPCfgReply_Promise {
 	return HPCfgReply_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
 }
 
-const schema_8fcd13516850d142 = "x\xda|V\x7flTY\x15>\xe7\xde\x99\xce\xefv" +
-	"\xc6\xfb\x8c\xdd\xb8\xb1\xbb\x1bL\xb6H7l\x81\xecn" +
-	"\x83\xf6\x07%\xcb,\x8b\xf6\xf5%\xeb\xee\xc6U\x87\x99" +
-	"7\xd3\xa1\xd3\xe9t\xde@S\x89\xa9\x9a\xa81\x8a\x04" +
-	"D\x03F\x02\x18\x08)\x88\x86&$\xa4\xa4(\x1a\x10" +
-	"\x1b!\x14\xc1\x10Pc\x11#!\x10\x91\xdf\x14\xea\xdb" +
-	"\x9c;\xd3\xf7\xa6o\xa6\xfdo\xe6~\xdf;\xe7\xbb\xe7" +
-	"\x9e\xfb\x9d\xbb4\xe0jc\xaf\xbb\xbf\xef\x06P\xdb\xdc" +
-	"5\xe6\x8b'[7\x0f\xdcT\xb7\x82\x1aF4;&" +
-	"\xbbzTqn+\xb8\xd1\x03 F\xd8\x848\xc6\xe8" +
-	"\xd7Q6\x08h\x8e\x7fQ\x99\x09\x1c\\\xfb\x13\x88\x84" +
-	"\xcb\xb9\x92\xf12\xff\xbdh\xe4\xf4\xeb\xb3\x9c\xb8'\xae" +
-	"\xbdqmt\xea\x83\xaa\xdc\x03|B\x1c\x95\xdc#\x92" +
-	"\xfb\x99\xef\xb9\xbe\xc9V\xe0n\x12\xc1m\xb2\x8b\x18>" +
-	"\xd7\x98\x88\xc8_!\xd7o\x00\xcd\x89\x0b?\x0fvL" +
-	"/\xd9\xe7\x10\xbc\x1a=\x11\x00\xf1\x03\xd7\x84\xf8)\xb1" +
-	"\x97ms\xfd\x96\x03\x9aS\xd7\xbf2z\xf6g\xbd\x07" +
-	"\x1c2d\xf2\x11\xefEq\xcc+\xb7\xe7\xa5\xd0?<" +
-	"\xb3\xfc\x97\xeb\x83G\x0e:\xb8\xb2\x14\x1f\xf8\xee\x09\xdd" +
-	"G\xbfb\xbeV@\xf3\xaf?N\xb6o\x9fy|\xa8" +
-	"\x1aw\x9b\xef\xaa\xd8-\xb9\xbb$w\xed\xaf\xb7<\xfa" +
-	"\xc3\xd0\xd3\xc3\xd5J\xf1'\xdf\x98\x98\x94\xdcs>*" +
-	"\xc5\x8f\x0e]\x0fM=\xda<Z-n\xa3\xff\xaaX" +
-	"\xe1\xa7_\xaf\xfb)n\xfd\xea\x1b_\x10\x7f|y\x8c" +
-	"J\xc1\x1ce\x8b\xf9/\x8a>\xc9M\xfb)n\x9bz" +
-	"\xe6\xc9\xad\xf3\xef\x8dW\x89\xbb\xec\xb4\xff\x13(.K" +
-	"\xf2\xa4\x0c\xbc\xed\xc2\xbe\xff\xf4No9\xeb\xa8\xb1\xac" +
-	"+\x06\x18\x8aP@\x1eM\x80\xc8_=\xfc\xb9\x13\x9f" +
-	"<\xb2\xf6b\x15\xb2h\x0aL\x88\xb7$w\x85\xe4n" +
-	"\xfat\xe7\xb3u\x8d;\xfe\x01\xea\xa7\x90\xdb\xc7\xaex" +
-	"\x90J\x1c\xb8\x05(>\x0a\x90\xdc\xf8\x0b\xdf8\xee\x0f" +
-	"{\xee;\xdbRF\x1d\x0f\x8c\x8a\xd3\x14u\xd9\xa9\xc0" +
-	"\x97\x11\xd0\xcc\xc5\x0a=_\xebK\xf5\xb1\xc2k\xf1X" +
-	".\x9bkY\xd3\xa5\xe9\xa9n}\x00\xa0\x0bQ\xf5r" +
-	"\x17\x80\x0b\x01\"\x8d\xcd\x00\xea\"\x8e\xeaR\x86\x88\x0a" +
-	"\xd2Z\xd3;\x00\xea\x12\x8e\xea\x1a\x86\x0d\x09\xa3\x10m" +
-	"G\x1f0\xf4\x01\x9a\xa9|\xff\xc6\\4a\x00\x00\xd6" +
-	"\x02vq\xc4\xb0]u@Z\xacL.S\xc7\x0dg" +
-	"\xea\xc5\xa5\xd4m\x0c#\xb3\xb9?\xdf\x0d\xa0\xae\xe4\xa8" +
-	"\xbe\xcf\xb0.\xaf\xc7\x0d;M\xdd\x98\x7f\xe5\xe6\xf7v" +
-	"\xee\x99Mct\xeb\x9b\xa2\xd9d?`\x19\xa9\xfb_" +
-	"\xd3o|\xf7\xed\xe6=\x0bk\xc9e\x86*\x0a\xf1\x8a" +
-	"]\x08KM\x13I|\x95\xa3\xba\x9c\xa1'\xaf\x0f`" +
-	"\xd8>\"@\x0cCIc\xd86\x82\xe2\xb2\x95\x17g" +
-	"\xf3\xb6\x16\xebOI\x83V\xd2\xd5T\xfd6\x8e\xea\xbb" +
-	"eI\xa3\xb4\xd8\xc9Q\xedb\x88\x0c\xcbz$\xb2\xae" +
-	"\x19X\x83\x91\x8f\xdb'2\xf7|\x1a\x92\x99X\xca\xa8" +
-	"\xdctW\xac\xd0\xb3.\xd5W(n\xba\x93\xbb\x82\xa6" +
-	"I\x02\xc49l\x06\xd0\xce\"G\xed\x122\x0c\xe1\xff" +
-	"M)BLb\x0b\x80\xf6g\x02\xae\x10\xc0fL\x05" +
-	"\x19\x80\xb8\x8c\xef\x00h\x97\x08\xf8'\x01\xfc\xb9\xa9 " +
-	"\x07\x10\x7f\x97_\\!\xe0\x06\x01\xaeg\xa6\x82.\x00" +
-	"1\x85\x1d\x00\xda\xdf\x08\xb8I\x80{\xdaT\xd0\x0d " +
-	"\xfe-C\xdd \xe0.\x015OM\x05k\x00\xc4\x1d" +
-	"\xfc\x10@\xbbM\xc0\x13\x02<OLE\xde\xf7\x87\xb8" +
-	"\x01@{\x80\x1c\xbb\x19\xc3\x90\xf7\xb1\xa9\xa0\x17@\xcc" +
-	"\xe0w\x00\xb4\xe7\xf4\x81\x97\x00\xdf#SA\x1f\x80p" +
-	"\xb3\xed\x00\x9a\x97q\xd4\x14\x02\xfc\x0fM\x05\xfd\x00\"" +
-	"\xc2\xf2\x00Z\x98\x80\x17\x09\x08<0\x15\x0c\x00\x88\x17" +
-	"\x18\x85\xaa'`\x11\x01\xc1\xfb\xa6\x82A\xb2sFj" +
-	"_\"`\x09\x01\xa1{\xa6\x82!2!Fj_%" +
-	"`9\x01\xb5\xff3\x15\xac%O\x92_,%`%" +
-	"\x01uwM\x05\xeb\x00\xc4[\x12x\x93\x80N\x02\xc2" +
-	"\xff5\x15\x0c\x03\x88v\x19\xaa\x8d\x80w\x19\xc3\x86\x8d" +
-	"YC/@M\xab!{\xa7\xb2\xfbL\xc3\xeaf\xc0" +
-	"\xb0=a\x8ah\xf1\xb3Tew\x0e\x1bzJ\x1b\xca" +
-	"\xc6\xab\xf4\xadu\xa9d\xc0\xf2\xdbDh:\xa9\x15b" +
-	"\x05\xbd\x1b\xb8T3\x92\xfc\xc5\xa9\xde\xc1;7\x1dp" +
-	"\x14\xea\xb2\xc9~\xba\x15'\x03\xcf\x1f6\x9f\x7f\xe9/" +
-	"erW\xf5\xc4\xb2)\xd4\x8dh\x82\xdc\x08\xc3\xb6\xb5" +
-	"V\xe7\xe428\x84a\xdb\xad\x1d,h\xd0\x8dbi" +
-	"\xacYU\x19\xa7X\xa2*\xbb\xed\xc9\xcd\xba\"\xa1\xd6" +
-	"\xe4\x9f\x8b\xe6\x80gH\x825\xe0\xe6\xc2\xa9\xe2\xc7\xd6" +
-	"X\xb5\xd0UI;\xb45\x12\xe6\xa2\xb3\xa1\xad\x19\xe7" +
-	"p\x0f\xa7}\xc7\xd1p\x18HG5\x03Y\\2\x90" +
-	"\xaf3\x8c0&\xefm\xe4#\xf2\xb7\xf79\xaa\x09\x86" +
-	"\xc3%\x1bwx\xb7eg\xf3Y\xaeG\xcf\xe71\x08" +
-	"\x0c\x83\xf38\xab,x\xf1DH\xa7\xcb\xd2\x19j\x01" +
-	"P\xbd\x1c\xd5EL\xb6e4ae\x09\x01\xab\xee\xd4" +
-	"\xa5=\xe7xf\xc8\x11l\xb1\x1d\xcc!\xb8\xfc\x14j" +
-	"\xe7\xb3\xe1h\xc2\x98\x7f\x08\xda\xde\xdfb{\x7f\x83\x94" +
-	",\xa5\x86\x00[\x93\x1b3\x19\xfbo5\xe5\xc5\xd3]" +
-	"Xy<\x99*Sn\xcd\xf7\xf9\x06\xd7\x9a\xae\xb7\xe5" +
-	"\xa9a\xc2!\xbe\xa3\\\xbc\xab$\xbe\xc3\x16?\xdc?" +
-	"\x98\xd5\xf3\xed\xda\xec\x90\xb0N\xdf\x03\x0c=e\xa9\xb8" +
-	"\xf3$\xed\x1b\xe8\xd8\xc6+\xf66<\xe9D\xd9.\xac" +
-	"\x87\x9dc\x17\xf3\x84\x1e\x00G`\x9a\xfeA\x8ej=" +
-	"C3\x133\x0a\xabz\xf48`/z\x81\xa1w\x81" +
-	"R\x97\xde5e\x916\x94E\x8a\xcb\x8c\x09\x0d\xea\xd2" +
-	"\xd9\xb8>\x7f\xb0\xa2\x19\xbc\x96\xac\xa3)J%\x0e\x7f" +
-	"\xabxy\xe66\xc8\xb7\xe5\xc0\x8b4u\x97\x9eIo" +
-	"R\x83\xa4\xd7\xe7c\x88\xc0\x90\x9e_\xf1X\xbcG\xff" +
-	"R6\x038d\xadU\xb4b\x83\xd4N\xb2\xeb-\xd9" +
-	"\xbb\xe8\xdcvpT\xf7\xda\xcf\xb1\xdd\xb4\xb6\x93\xa3\xba" +
-	"\xbf\xec:\xef\xa37\xda^\x8e\xea\xaf\x18F8\x16\x15" +
-	"\x8d\x10s?G\xf5w\x0c#.&\xc7od\x9c\x16" +
-	"\x8fsT/1\x8c\xb8\xb9\x1c\xbd\x91\xc9\x0f\x01\xd4\x0b" +
-	"\x1c\xd5\xdb\x0b\xb9\xc1\xf0&=o\xa4\xfb\xb3V\xc5d" +
-	"'E\xb5N2\xb5R\xf7\x0c\x0f\xe6\xd3\x05=o\xf5" +
-	"\x80\xafx\x9d\x87\xf3z,Q\xb9l\xe6\xf5T\xda(" +
-	"\xe4\xd3\xc0u'\xf6q\x00\x00\x00\xff\xff4\x06O\xa2"
+const schema_8fcd13516850d142 = "x\xda|U[lT\xd5\x1a\xfe\xff\xb5\xa6\xdd3\xed" +
+	"\x94\xced\xcd\xc9\xe1\xe4\x9c\x9c\x8a\xc1D\x90\x12\xa8\x10" +
+	"\x94\xa0\xbdHC\x87\x8b\xce\xea\xa0\x02\x01e\x9c\xd9s" +
+	"\x81\xe9t\xba\xf7\x94Z\x91\x14\x131\x9a\x80\x18E\x83" +
+	"F\x02M \x04\xf0\x92\xf4\x89\x94@\xbc<\xa8M0" +
+	"\x14\xc5\x07HL@M\x88\x91\x07\xa3\x80\xdc\xdc\xe6_" +
+	"3\xb3\xf7tO\xdb\xb7\xbd\xd7\xf7_\xbe\xf5_\xbe\xb5" +
+	"@\xf26\xb6\xb0f\xc8\x03 \x17\xd5\xd4Z\xff;\xd3" +
+	"\xba\xbd\xef\xaa\xdc\x0b2\x80hu\x8cG\xd2R\x9c\xdd" +
+	"\x0b5\xa8\x01\x88\x07\xd8\x98X\xc8\xe8\xab\x99\x0d\x00Z" +
+	"\xa7\x9f\x0c\xdd\xab?\xba\xea\x1d\x08\x06*m\x95\xc5\x01" +
+	"\xf6\x858\xa2\xbe\x86\x95\xed\xa9KK.\x8d\\^?" +
+	"\xa9\xed,>&\x9a9}\xcd\xe1d\xfb\xff\xd7<;" +
+	"\xd8b<@$\xb8c\xec!\x8b]|T\xecQ\xb6" +
+	"o\xf0O\x01\xad\xb1s\x1f\xf8;n\xcf\x1bv\x11\xee" +
+	"D\xcd\x0f j<c\"H~\x0f7x\x8e2@" +
+	"\xeb\xf2\x95\x8d#_\xbf\xb7\xf5\x88\x8b\x86\x0a\xf8K\xed" +
+	"y\xf1{-}]\xab\xa5\xd0?\xbc\x99l\x7f\xfb\xde" +
+	"\xcd\xe3.[U\x8aw\xb5\x8bbXS\x17\xd5Z\x01" +
+	"\xadU\x9f\xec\xb9\xf1\xe5\xe0\xad\x13\x93]\xef\xac6*" +
+	".(\xdbq\x8d\xae\xb7\xfb\xf8\x95\x86\xcb7\xb6\x8fL" +
+	"\x16\xb7\xd9{Q<\xea\xa5\xaf\xc5^\x8a;\xb3\xf3\xe7" +
+	"\xc7\xc5W\xb3F\xe9z\xccU\x0a\xdd{^\xf4)\xdb" +
+	"\x1e/\xc5}\xee\xc4C\xa7\xfe\xf5\xf1\xaa\xf3\xaeR(" +
+	"\xdbo\xbcc\xe2\x82\xb2\x1dWq\xb7\xfdw\xf9\x9d5" +
+	"s\xf6\xfd\x08\xf2\xdf\xc8\x9d\x82\x874\x04\x10\xd7\xbd\xbf" +
+	"\x02\x8a[*h\xfc?/\x9d\xac\x0bh\x7f\xb8\x07B" +
+	"E]\xef\x1b\x111\x1f\x95w\x93\xefY\x04\xb4\xf2\xb1" +
+	"B\xfa\xf9\x9eT\x0f+\xcc\x8f\xc7\xf2\xb9\xfc\xd2\xaeH" +
+	"TOu\xeb}\x00\x11D\xe9\xe5\x1e\x00\x0f\x02\x04\xe7" +
+	"\xb4\x00\xc8\xd9\x1c\xe5\x02\x86\x88!\xa4\xb3\xe6\x95\x00r" +
+	"\x1eG\xd9\xc5\xb0)a\x16\xc2\xed\xe8\x03\x86>@+" +
+	"e\xf4\xf6\xe7\xc3\x09\x13\x00p\x06`\x84#\x06\x9c\xda" +
+	"\x00\xd2aur\x95:n\xbaS\xcf-\xa5nc\x18" +
+	",\xe7~\xac\x1b@.\xe3(\xd71l4\xf4\xb8\xe9" +
+	"\xa4i\x1c\xad[\xb6\xfd\x99\xfd\x07\xcbi\xccn}[" +
+	"8\x97\xec\x05\xac0\xea\xfe\xe9\xf6\x92]+Z\x0eN" +
+	"\xcf%\x9f\x1d\xac*\xc4\xfdN!l6\xcdD\xf1A" +
+	"\x8er\x11C\xcd\xd0\xfb0\xe0\xb4\x08\x10\x03P\xe2\x18" +
+	"pV\xb0xl\xe7\xc5r\xde\xd6b\xfd)\xa9\xdfN" +
+	"\xdaI\xd5o\xe3(WW$\x0d\xd3\xe1r\x8e2\xc2" +
+	"\x10\x19V\xccHpM\x0b\xb0&\xd3\x88;\x1d\x99\xd8" +
+	"\x9f\xa6d6\x962\xab/\x1d\x89\x15\xd2kR=\x85" +
+	"\xe2\xa5\x17p\x8f\xdf\xb2\x88\x80h\xc7\x16\x80\xe82\xe4" +
+	"\x18\xedB\x86\x0d\xf8\xb7\xa5H\x88N\\\x0a\x10m#" +
+	"`5\x01\xec\x9e\x15B\x06 \xc2\xb8\x12 \xdaE\xc0" +
+	"Z\x02\xf8]+\x84\x1c@H\xe5\xb1\x9a\x80u\x04x" +
+	"\xeeX!\xf4\x00\x88\xa7\x95\xc7Z\x026\x13Ps\xdb" +
+	"\x0aa\x0d\x80\xd8\x84\x1b\x00\xa2\x1b\x09H\x13P{\xcb" +
+	"\x0aa--\x14n\x01\x88&\x08\xc8\x13\xa0\xfde\x85" +
+	"\xd4V\xf6\xa8PY\x02^$\xc0{\xd3\x0a\xa1\x17@" +
+	"\xf4\xabP\x05\x02v\x12\xe0\xbba\x85\xd0\x07 v(" +
+	"\x8f\x97\x09x\x9d\x80\xba\xebV\x08\xebH\xbf\x14\xf0*" +
+	"\x01o\x11P\xff\xa7\x15\xc2z\x00\xb1G\x85\xdaM\xc0" +
+	"~d\xd8\xd4\x9f3\xf5\x02\xd4\xb6\x9a\xaa\x7f\xd5\x13`" +
+	"\x99\xf6D\x01\x06\x1c}-\xa2E\xb7\xd4$\x13b\x8f" +
+	"\xafr\xab\x9c[B3\xc9h!V\xd0\xbb\x81\xab\x9c" +
+	"\xc7\x92\x1f~\xbeu\xe0\xdaU\x17\x1c\x86\xc6\\\xb2\x97" +
+	"\xe6\xefL\xfd\xdd\xeb-\xdf\xde\xf7]\xd9 \x9d/\xef" +
+	";E\xb7_\x93\x89h\x1exv\x10\x03\x8e\xc0N\x84" +
+	"SEg[\xaam\xf4\x89\xa4\x13\xda\x16\xbb\x89h9" +
+	"\xb4\xad\xb1\xae\xbdp\x0bS\x1cM\xd7jtL\xb6\x1a" +
+	"sK\xab\xb1\x99a\x9015\x91\xc1M\xb4\xb9\xeb8" +
+	"\xca\x04\xc3\xa1\x92@\xb9T\xc9^\xd4\xa9\xc4D\xd3\x0d" +
+	"\x03\xfd\xc0\xd0?\x0d\xc7<\xcf\x0e\x12I\x8fM\xb2\x81" +
+	"\xf8x9\xca\xd9UjUY\xb5\x19S\x09B8a" +
+	"N-\xc7\x8e\x0a-uT\xa8\xc9$/l\x00\x86\x0d" +
+	"\x80\xad\xc9\xfel\xd6\xf9\x9d\x8cy\xb1\x1b\xd33\x8f'" +
+	"S\x15\xcc\xed\x97f*\x09\xed\x8a\xacPU\xc6\x84\x8b" +
+	"|G%yO\x89|\x87C~\xa8w \xa7\x1b\xed" +
+	"\xd1\xb2\\\xd9\xdd\xd2\x80\xa16\x0d\xff\xd2\xb3UA\x7f" +
+	"\x0b\x80\xf4s\x943\x19Z\xf1t,\x97\xd2\x13Qh" +
+	"\xcc\xe4\xe2:z\x81\xa1wJ\xe9\xef\x9b\x9fl$\x91" +
+	"$\xde\x81\x9d\xc5\x09\x9aX\xf5W\x94\x9e\x05\x9b\xbbK" +
+	"\xaf\xe0#T\xf5\xcc\x0bF\x0c\x11\x18\xd2\xeb\x1a\x8f\xc5" +
+	"\xd3\xfaS\xb9,\xe0\xa0}V\xd5\xdf&\xc5\x9dh\xcf" +
+	"\xb4i\xbfO\xc5\xd8\xc7Q\x1er^\xdb\x03t\xb6\x9f" +
+	"\xa3<\\1\xd3\xc3\xf4\x04\x1f\xe2(?b\x18\xe4X" +
+	"dt\x8c,\x0fs\x94\x9f1\x0cz\x98R\xd7\xe0i" +
+	":<\xc9Q~\xcf0X\xc3\x95\xb2\x06\xc77\x00\xc8" +
+	"s\x1c\xe5o\xd3\xad\xc4\xd06\xdd03\xbd9\xbbb" +
+	"\xaa=\xe1\xe8r\xda\xecRK\x86\x06\x8cLA7\xec" +
+	"\xf1\xa0\xce\xcd\x00\x1c2\xf4X\xa2\xfa\xd82\xf4T\xc6" +
+	",\x18\x19\xe0\xba\x1b\xfb'\x00\x00\xff\xff\x16\xfe\x95\x8d"
 
 func init() {
 	schemas.Register(schema_8fcd13516850d142,
@@ -2092,13 +1658,10 @@ func init() {
 		0x9d0135027d04861e,
 		0xa12cfa420c9ad0ca,
 		0xa56b96c8b25ce2e1,
-		0xa7ad0c62a234c68b,
 		0xaaf7fd9241668ed6,
 		0xabf979c3f68dae4b,
 		0xb27bf6e10de2aa8c,
 		0xba21c7133ee44518,
-		0xbd56ceeaf8c65140,
-		0xc88dfa6be7a1d091,
 		0xd24bad15bb2bab5e,
 		0xde94294dfb441b76,
 		0xf307100ab87a1963)
diff --git a/proto/path_mgmt.capnp b/proto/path_mgmt.capnp
index acf74f08c..adcdeb6f6 100644
--- a/proto/path_mgmt.capnp
+++ b/proto/path_mgmt.capnp
@@ -26,24 +26,11 @@ struct SegReply {
     recs @1 :SegRecs;
 }
 
-struct SegChangesIdReq {
-    # Timestamp of last check, seconds since Unix Epoch
-    lastCheck @0 :UInt32;
-}
-
 struct SegIds {
     segId @0 :Data;
     fullId @1 :Data;
 }
 
-struct SegChangesIdReply {
-    ids @0 :List(SegIds);
-}
-
-struct SegChangesReq {
-    segIds @0 :List(Data);
-}
-
 struct HPGroupId {
     ownerAS @0 :UInt64;
     groupId @1 :UInt16;
@@ -87,18 +74,13 @@ struct PathMgmt {
         segReq @1 :SegReq;
         segReply @2 :SegReply;
         segReg @3 :SegRecs;
-        segSync @4 :SegRecs;
-        sRevInfo @5 :Sign.SignedBlob;
-        ifStateReq @6 :IFState.IFStateReq;
-        ifStateInfos @7 :IFState.IFStateInfos;
-        segChangesIdReq @8 :SegChangesIdReq;
-        segChangesIdReply @9 :SegChangesIdReply;
-        segChangesReq @10 :SegChangesReq;
-        segChangesReply @11 :SegRecs;
-        hpSegReq @12 :HPSegReq;
-        hpSegReply @13 :HPSegReply;
-        hpSegReg @14 :HPSegRecs;
-        hpCfgReq @15 :HPCfgReq;
-        hpCfgReply @16 :HPCfgReply;
+        sRevInfo @4 :Sign.SignedBlob;
+        ifStateReq @5 :IFState.IFStateReq;
+        ifStateInfos @6 :IFState.IFStateInfos;
+        hpSegReq @7 :HPSegReq;
+        hpSegReply @8 :HPSegReply;
+        hpSegReg @9 :HPSegRecs;
+        hpCfgReq @10 :HPCfgReq;
+        hpCfgReply @11 :HPCfgReply;
     }
 }
diff --git a/scion.sh b/scion.sh
index 149f2fc0a..94151499f 100755
--- a/scion.sh
+++ b/scion.sh
@@ -243,7 +243,7 @@ py_test() {
 }
 
 bazel_test() {
-    bazel test //go/... --print_relative_test_log_paths
+    bazel test //go/... --test_output=errors --print_relative_test_log_paths
 }
 
 cmd_coverage(){


Test patch:
diff --git a/go/cs/segreq/authoritative_test.go b/go/cs/segreq/authoritative_test.go
new file mode 100644
index 000000000..465548305
--- /dev/null
+++ b/go/cs/segreq/authoritative_test.go
@@ -0,0 +1,134 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/proto"
+)
+
+func TestAuthoritativeClassify(t *testing.T) {
+	type request struct {
+		Src addr.IA
+		Dst addr.IA
+	}
+	tests := map[string]struct {
+		LocalIA         addr.IA
+		Request         request
+		ErrorAssertion  require.ErrorAssertionFunc
+		ExpectedSegType proto.PathSegType
+	}{
+		"Invalid Src": {
+			LocalIA: core110,
+			Request: request{
+				Src: core210,
+				Dst: core110,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Invalid Src Wildcard": {
+			LocalIA: core110,
+			Request: request{
+				Src: addr.IA{I: 1, A: 0},
+				Dst: core210,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Invalid Dst": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: addr.IA{I: 0, A: 0},
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Core Local ISD": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: core120,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Remote ISD": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: core210,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Wildcard Local ISD": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: addr.IA{I: 1, A: 0},
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Wildcard Remote ISD": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: addr.IA{I: 2, A: 0},
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Remote ISD Non-Core ": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: nonCore211,
+			},
+			// core/non-core dst in remote ISD unchecked! Returning an error would be ok too...
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Down": {
+			LocalIA: core110,
+			Request: request{
+				Src: core110,
+				Dst: nonCore111,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_down,
+		},
+	}
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			p := &authoritativeProcessor{
+				localIA:     test.LocalIA,
+				coreChecker: newMockCoreChecker(ctrl),
+			}
+			segType, err := p.classify(context.Background(), test.Request.Src, test.Request.Dst)
+			test.ErrorAssertion(t, err)
+			assert.Equal(t, test.ExpectedSegType, segType)
+		})
+	}
+}
diff --git a/go/cs/segreq/db_test.go b/go/cs/segreq/db_test.go
deleted file mode 100644
index dce516343..000000000
--- a/go/cs/segreq/db_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq_test
-
-import (
-	"context"
-	"errors"
-	"testing"
-	"time"
-
-	"github.com/golang/mock/gomock"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/scionproto/scion/go/cs/segreq"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher/mock_segfetcher"
-	"github.com/scionproto/scion/go/lib/pathdb/mock_pathdb"
-	"github.com/scionproto/scion/go/lib/xtest"
-)
-
-func TestPSPathDBGetNextQuery(t *testing.T) {
-	tests := map[string]struct {
-		Src                     addr.IA
-		Dst                     addr.IA
-		PreparePathDB           func(db *mock_pathdb.MockPathDB, src, dst addr.IA)
-		PrepareLocalInfo        func(i *mock_segfetcher.MockLocalInfo, src, dst addr.IA)
-		ErrorAssertion          require.ErrorAssertionFunc
-		AssertNextQueryAfterNow assert.BoolAssertionFunc
-	}{
-		"LocalInfo error": {
-			Src:           xtest.MustParseIA("1-ff00:0:111"),
-			Dst:           xtest.MustParseIA("1-ff00:0:120"),
-			PreparePathDB: func(db *mock_pathdb.MockPathDB, src, dst addr.IA) {},
-			PrepareLocalInfo: func(i *mock_segfetcher.MockLocalInfo, src, dst addr.IA) {
-				i.EXPECT().IsSegLocal(gomock.Any(), src, dst).
-					Return(false, errors.New("test err"))
-			},
-			ErrorAssertion:          require.Error,
-			AssertNextQueryAfterNow: assert.False,
-		},
-		"Is Local": {
-			Src:           xtest.MustParseIA("1-ff00:0:111"),
-			Dst:           xtest.MustParseIA("1-ff00:0:120"),
-			PreparePathDB: func(db *mock_pathdb.MockPathDB, src, dst addr.IA) {},
-			PrepareLocalInfo: func(i *mock_segfetcher.MockLocalInfo, src, dst addr.IA) {
-				i.EXPECT().IsSegLocal(gomock.Any(), src, dst).
-					Return(true, nil)
-			},
-			ErrorAssertion:          require.NoError,
-			AssertNextQueryAfterNow: assert.True,
-		},
-		"Non local": {
-			Src: xtest.MustParseIA("1-ff00:0:111"),
-			Dst: xtest.MustParseIA("1-ff00:0:120"),
-			PreparePathDB: func(db *mock_pathdb.MockPathDB, src, dst addr.IA) {
-				db.EXPECT().GetNextQuery(gomock.Any(), src, dst, gomock.Any()).
-					Return(time.Now().Add(time.Hour), nil)
-			},
-			PrepareLocalInfo: func(i *mock_segfetcher.MockLocalInfo, src, dst addr.IA) {
-				i.EXPECT().IsSegLocal(gomock.Any(), src, dst).
-					Return(false, nil)
-			},
-			ErrorAssertion:          require.NoError,
-			AssertNextQueryAfterNow: assert.True,
-		},
-	}
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			ctrl := gomock.NewController(t)
-			defer ctrl.Finish()
-			pdb := mock_pathdb.NewMockPathDB(ctrl)
-			li := mock_segfetcher.NewMockLocalInfo(ctrl)
-			test.PreparePathDB(pdb, test.Src, test.Dst)
-			test.PrepareLocalInfo(li, test.Src, test.Dst)
-			db := &segreq.PathDB{
-				PathDB:    pdb,
-				LocalInfo: li,
-			}
-			nq, err := db.GetNextQuery(context.Background(), test.Src, test.Dst, nil)
-			test.ErrorAssertion(t, err)
-			test.AssertNextQueryAfterNow(t, nq.After(time.Now()))
-		})
-	}
-}
diff --git a/go/cs/segreq/forwarder_test.go b/go/cs/segreq/forwarder_test.go
new file mode 100644
index 000000000..77748f16f
--- /dev/null
+++ b/go/cs/segreq/forwarder_test.go
@@ -0,0 +1,228 @@
+// Copyright 2020 ETH Zurich
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package segreq
+
+import (
+	"context"
+	"testing"
+
+	"github.com/golang/mock/gomock"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"github.com/scionproto/scion/go/lib/addr"
+	"github.com/scionproto/scion/go/lib/xtest"
+	"github.com/scionproto/scion/go/pkg/trust"
+	"github.com/scionproto/scion/go/pkg/trust/mock_trust"
+	"github.com/scionproto/scion/go/proto"
+)
+
+var (
+	isd1 = xtest.MustParseIA("1-0")
+	isd2 = xtest.MustParseIA("2-0")
+
+	core110 = xtest.MustParseIA("1-ff00:0:110")
+	core120 = xtest.MustParseIA("1-ff00:0:120")
+	core130 = xtest.MustParseIA("1-ff00:0:130")
+	core210 = xtest.MustParseIA("2-ff00:0:210")
+
+	nonCore111 = xtest.MustParseIA("1-ff00:0:111")
+	nonCore112 = xtest.MustParseIA("1-ff00:0:112")
+	nonCore211 = xtest.MustParseIA("2-ff00:0:211")
+	nonCore212 = xtest.MustParseIA("2-ff00:0:212")
+
+	cores = map[addr.IA]struct{}{
+		core110: {},
+		core120: {},
+		core130: {},
+		core210: {},
+	}
+)
+
+// newMockCoreChecker creates a CoreChecker with a mock ASInspector that checks the IAs
+// in the cores map above.
+func newMockCoreChecker(ctrl *gomock.Controller) CoreChecker {
+
+	inspector := mock_trust.NewMockInspector(ctrl)
+	inspector.EXPECT().HasAttributes(gomock.Any(), gomock.Any(), trust.Core).DoAndReturn(
+		func(_ context.Context, ia addr.IA, _ trust.Attribute) (bool, error) {
+			_, ok := cores[ia]
+			return ok, nil
+		},
+	).AnyTimes()
+	return CoreChecker{Inspector: inspector}
+}
+
+func TestForwarderClassify(t *testing.T) {
+	type request struct {
+		Src addr.IA
+		Dst addr.IA
+	}
+	tests := map[string]struct {
+		LocalIA         addr.IA
+		Request         request
+		ErrorAssertion  require.ErrorAssertionFunc
+		ExpectedSegType proto.PathSegType
+	}{
+		"Invalid Src": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: addr.IA{I: 0, A: nonCore111.A},
+				Dst: core110,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Invalid Dst": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: nonCore111,
+				Dst: addr.IA{I: 0, A: 0},
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Core Wildcards Src & Dst": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: addr.IA{I: 1, A: 0},
+				Dst: addr.IA{I: 2, A: 0},
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Wildcard Src": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: addr.IA{I: 1, A: 0},
+				Dst: core210,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Wildcard Dst": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core110,
+				Dst: addr.IA{I: 2, A: 0},
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_core,
+		},
+		"Core Invalid Src": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core210, // Src not in local ISD
+				Dst: core110,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Down": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core110,
+				Dst: nonCore112,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_down,
+		},
+		"Down Wildcard": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: addr.IA{I: 1, A: 0},
+				Dst: nonCore112,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_down,
+		},
+		"Down Remote ISD": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core210,
+				Dst: nonCore212,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_down,
+		},
+		"Down Remote ISD Wildcard": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: addr.IA{I: 2, A: 0},
+				Dst: nonCore212,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_down,
+		},
+		"Down wrong ISD": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core110,
+				Dst: nonCore211,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Down Invalid Dst Local": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: core110,
+				Dst: nonCore111,
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Up": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: nonCore111,
+				Dst: core110,
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_up,
+		},
+		"Up Wildcard": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: nonCore111,
+				Dst: addr.IA{I: 1, A: 0},
+			},
+			ErrorAssertion:  require.NoError,
+			ExpectedSegType: proto.PathSegType_up,
+		},
+		"Up Invalid Src": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: nonCore112,
+				Dst: addr.IA{I: 1, A: 0},
+			},
+			ErrorAssertion: require.Error,
+		},
+		"Invalid Non-Core to Non-Core": {
+			LocalIA: nonCore111,
+			Request: request{
+				Src: nonCore111,
+				Dst: nonCore112,
+			},
+			ErrorAssertion: require.Error,
+		},
+	}
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			f := &forwarder{localIA: test.LocalIA, coreChecker: newMockCoreChecker(ctrl)}
+			segType, err := f.classify(context.Background(), test.Request.Src, test.Request.Dst)
+			test.ErrorAssertion(t, err)
+			assert.Equal(t, test.ExpectedSegType, segType)
+		})
+	}
+}
diff --git a/go/cs/segreq/provider_test.go b/go/cs/segreq/provider_test.go
deleted file mode 100644
index f58f5eb81..000000000
--- a/go/cs/segreq/provider_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq_test
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	"github.com/golang/mock/gomock"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/scionproto/scion/go/cs/segreq"
-	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
-	"github.com/scionproto/scion/go/lib/ctrl/seg"
-	"github.com/scionproto/scion/go/lib/pathdb/mock_pathdb"
-	"github.com/scionproto/scion/go/lib/pathdb/query"
-	"github.com/scionproto/scion/go/lib/revcache"
-	"github.com/scionproto/scion/go/lib/revcache/mock_revcache"
-)
-
-func TestSegSelector(t *testing.T) {
-	tests := map[string]struct {
-		PrepareMocks func(db *mock_pathdb.MockPathDB,
-			c *mock_revcache.MockRevCache) []*seg.PathSegment
-		ErrorAssertion require.ErrorAssertionFunc
-	}{
-		"PathDB error": {
-			PrepareMocks: func(db *mock_pathdb.MockPathDB,
-				c *mock_revcache.MockRevCache) []*seg.PathSegment {
-
-				db.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, errors.New("test err"))
-				return nil
-			},
-			ErrorAssertion: require.Error,
-		},
-		"RevCacheError": {
-			PrepareMocks: func(db *mock_pathdb.MockPathDB,
-				c *mock_revcache.MockRevCache) []*seg.PathSegment {
-
-				results := query.Results{
-					&query.Result{Seg: &seg.PathSegment{}},
-				}
-				db.EXPECT().Get(gomock.Any(), gomock.Any()).Return(results, nil)
-				c.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, errors.New("test err"))
-				return nil
-			},
-			ErrorAssertion: require.Error,
-		},
-		"No segments": {
-			PrepareMocks: func(db *mock_pathdb.MockPathDB,
-				c *mock_revcache.MockRevCache) []*seg.PathSegment {
-				db.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil)
-				return nil
-			},
-			ErrorAssertion: require.Error,
-		},
-		"Segments": {
-			PrepareMocks: func(db *mock_pathdb.MockPathDB,
-				c *mock_revcache.MockRevCache) []*seg.PathSegment {
-
-				seg1 := &seg.PathSegment{RawSData: []byte{1}}
-				seg2 := &seg.PathSegment{RawSData: []byte{2}}
-				seg3 := &seg.PathSegment{RawSData: []byte{3}}
-				results := query.Results{
-					&query.Result{Seg: seg1},
-					&query.Result{Seg: seg2},
-					&query.Result{Seg: seg3},
-				}
-				db.EXPECT().Get(gomock.Any(), gomock.Any()).Return(results, nil)
-				r1 := c.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil)
-				r2 := c.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, nil).After(r1)
-				c.EXPECT().Get(gomock.Any(), gomock.Any()).Return(
-					revcache.Revocations{revcache.Key{}: &path_mgmt.SignedRevInfo{}}, nil).After(r2)
-				return []*seg.PathSegment{seg1, seg2}
-			},
-			ErrorAssertion: require.NoError,
-		},
-	}
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			ctrl := gomock.NewController(t)
-			defer ctrl.Finish()
-			db := mock_pathdb.NewMockPathDB(ctrl)
-			c := mock_revcache.NewMockRevCache(ctrl)
-			possibleSegs := test.PrepareMocks(db, c)
-			s := segreq.SegSelector{
-				PathDB:   db,
-				RevCache: c,
-			}
-			seg, err := s.SelectSeg(context.Background(), &query.Params{})
-			test.ErrorAssertion(t, err)
-			if len(possibleSegs) > 0 {
-				assert.Contains(t, possibleSegs, seg)
-			}
-		})
-	}
-}
diff --git a/go/cs/segreq/splitter_test.go b/go/cs/segreq/splitter_test.go
deleted file mode 100644
index 57e7ff244..000000000
--- a/go/cs/segreq/splitter_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2019 Anapaya Systems
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package segreq_test
-
-import (
-	"context"
-	"errors"
-	"testing"
-
-	"github.com/golang/mock/gomock"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-
-	"github.com/scionproto/scion/go/cs/segreq"
-	"github.com/scionproto/scion/go/lib/addr"
-	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
-	"github.com/scionproto/scion/go/lib/xtest"
-	"github.com/scionproto/scion/go/pkg/trust/mock_trust"
-)
-
-func TestSplitter(t *testing.T) {
-	ia110 := xtest.MustParseIA("1-ff00:0:110")
-	ia120 := xtest.MustParseIA("1-ff00:0:120")
-	ia111 := xtest.MustParseIA("1-ff00:0:111")
-	ia112 := xtest.MustParseIA("1-ff00:0:112")
-	isd1 := addr.IA{I: 1}
-
-	tests := map[string]struct {
-		Req            segfetcher.Request
-		PrepareMock    func(i *mock_trust.MockInspector)
-		ErrorAssertion require.ErrorAssertionFunc
-		ExpectedSet    segfetcher.RequestSet
-	}{
-		"Empty request": {
-			Req:            segfetcher.Request{},
-			PrepareMock:    func(i *mock_trust.MockInspector) {},
-			ErrorAssertion: require.Error,
-			ExpectedSet:    segfetcher.RequestSet{},
-		},
-		"Non-core to core": {
-			Req: segfetcher.Request{Src: ia111, Dst: isd1},
-			PrepareMock: func(i *mock_trust.MockInspector) {
-				i.EXPECT().HasAttributes(gomock.Any(), ia111, gomock.Any()).Return(false, nil)
-			},
-			ErrorAssertion: require.NoError,
-			ExpectedSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: ia111, Dst: isd1},
-			},
-		},
-		"Core to non-core": {
-			Req: segfetcher.Request{Src: ia120, Dst: ia111},
-			PrepareMock: func(i *mock_trust.MockInspector) {
-				i.EXPECT().HasAttributes(gomock.Any(), ia111, gomock.Any()).Return(false, nil)
-				i.EXPECT().HasAttributes(gomock.Any(), ia120, gomock.Any()).Return(true, nil)
-			},
-			ErrorAssertion: require.NoError,
-			ExpectedSet: segfetcher.RequestSet{
-				Down: segfetcher.Request{Src: ia120, Dst: ia111},
-			},
-		},
-		"Core to core": {
-			Req: segfetcher.Request{Src: ia110, Dst: ia120},
-			PrepareMock: func(i *mock_trust.MockInspector) {
-				i.EXPECT().HasAttributes(gomock.Any(), ia110, gomock.Any()).Return(true, nil)
-				i.EXPECT().HasAttributes(gomock.Any(), ia120, gomock.Any()).Return(true, nil)
-			},
-			ErrorAssertion: require.NoError,
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: segfetcher.Requests{{Src: ia110, Dst: ia120}},
-			},
-		},
-		"Non-core to non-core": {
-			Req: segfetcher.Request{Src: ia111, Dst: ia112},
-			PrepareMock: func(i *mock_trust.MockInspector) {
-				i.EXPECT().HasAttributes(gomock.Any(), ia111, gomock.Any()).Return(false, nil)
-				i.EXPECT().HasAttributes(gomock.Any(), ia112, gomock.Any()).Return(false, nil)
-			},
-			ErrorAssertion: require.Error,
-			ExpectedSet:    segfetcher.RequestSet{},
-		},
-		"Inspector error": {
-			Req: segfetcher.Request{Src: ia110, Dst: ia120},
-			PrepareMock: func(i *mock_trust.MockInspector) {
-				i.EXPECT().HasAttributes(gomock.Any(), ia110, gomock.Any()).Return(true, nil)
-				i.EXPECT().HasAttributes(gomock.Any(), ia120, gomock.Any()).
-					Return(false, errors.New("test error"))
-			},
-			ErrorAssertion: require.Error,
-			ExpectedSet:    segfetcher.RequestSet{},
-		},
-	}
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			ctrl := gomock.NewController(t)
-			defer ctrl.Finish()
-			i := mock_trust.NewMockInspector(ctrl)
-			test.PrepareMock(i)
-			splitter := segreq.Splitter{ASInspector: i}
-			rs, err := splitter.Split(context.Background(), test.Req)
-			test.ErrorAssertion(t, err)
-			assert.Equal(t, test.ExpectedSet, rs)
-		})
-	}
-}
diff --git a/go/lib/infra/modules/segfetcher/fetcher_test.go b/go/lib/infra/modules/segfetcher/fetcher_test.go
index 0fea68936..4d8c51277 100644
--- a/go/lib/infra/modules/segfetcher/fetcher_test.go
+++ b/go/lib/infra/modules/segfetcher/fetcher_test.go
@@ -24,14 +24,12 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/scionproto/scion/go/lib/ctrl/seg"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher"
 	"github.com/scionproto/scion/go/lib/infra/modules/segfetcher/mock_segfetcher"
 	"github.com/scionproto/scion/go/lib/pathdb/mock_pathdb"
 )
 
 type TestableFetcher struct {
-	Splitter      *mock_segfetcher.MockSplitter
 	Resolver      *mock_segfetcher.MockResolver
 	Requester     *mock_segfetcher.MockRequester
 	ReplyHandler  *mock_segfetcher.MockReplyHandler
@@ -41,7 +39,6 @@ type TestableFetcher struct {
 
 func NewTestFetcher(ctrl *gomock.Controller) *TestableFetcher {
 	return &TestableFetcher{
-		Splitter:      mock_segfetcher.NewMockSplitter(ctrl),
 		Resolver:      mock_segfetcher.NewMockResolver(ctrl),
 		Requester:     mock_segfetcher.NewMockRequester(ctrl),
 		ReplyHandler:  mock_segfetcher.NewMockReplyHandler(ctrl),
@@ -52,7 +49,6 @@ func NewTestFetcher(ctrl *gomock.Controller) *TestableFetcher {
 
 func (f *TestableFetcher) Fetcher() *segfetcher.Fetcher {
 	return &segfetcher.Fetcher{
-		Splitter:      f.Splitter,
 		Resolver:      f.Resolver,
 		Requester:     f.Requester,
 		ReplyHandler:  f.ReplyHandler,
@@ -69,41 +65,30 @@ func TestFetcher(t *testing.T) {
 
 	tests := map[string]struct {
 		PrepareFetcher func(*TestableFetcher)
-		Request        segfetcher.Request
+		Requests       segfetcher.Requests
 		ErrorAssertion require.ErrorAssertionFunc
 		ExpectedSegs   segfetcher.Segments
 	}{
-		"Splitter error": {
-			PrepareFetcher: func(f *TestableFetcher) {
-				f.Splitter.EXPECT().Split(gomock.Any(), gomock.Any()).
-					Return(segfetcher.RequestSet{}, testErr)
-			},
-			ErrorAssertion: require.Error,
-		},
 		"Resolver error": {
 			PrepareFetcher: func(f *TestableFetcher) {
-				f.Splitter.EXPECT().Split(gomock.Any(), gomock.Any())
 				f.Resolver.EXPECT().Resolve(gomock.Any(), gomock.Any(), gomock.Any()).
-					Return(segfetcher.Segments{}, segfetcher.RequestSet{}, testErr)
+					Return(segfetcher.Segments{}, segfetcher.Requests{}, testErr)
 			},
 			ErrorAssertion: require.Error,
+			ExpectedSegs:   segfetcher.Segments{},
 		},
 		"Immediately resolved": {
+			Requests: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: core_130},
+			},
 			PrepareFetcher: func(f *TestableFetcher) {
-				reqSet := segfetcher.RequestSet{
-					Up: segfetcher.Request{Src: non_core_111, Dst: core_130},
-				}
-				f.Splitter.EXPECT().Split(gomock.Any(), gomock.Any()).
-					Return(reqSet, nil)
-				f.Resolver.EXPECT().Resolve(gomock.Any(), gomock.Any(), gomock.Eq(reqSet)).
-					Return(segfetcher.Segments{Up: seg.Segments{tg.seg130_111}},
-						segfetcher.RequestSet{}, nil)
+				f.Resolver.EXPECT().Resolve(gomock.Any(), gomock.Any(), gomock.Any()).
+					Return(segfetcher.Segments{tg.seg130_111_up},
+						segfetcher.Requests{}, nil)
 			},
 			ErrorAssertion: require.NoError,
-			ExpectedSegs:   segfetcher.Segments{Up: seg.Segments{tg.seg130_111}},
+			ExpectedSegs:   segfetcher.Segments{tg.seg130_111_up},
 		},
-		// XXX(lukedirtwalker): testing the full loop is quite involved, and is
-		// therefore currently omitted.
 	}
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
@@ -113,7 +98,7 @@ func TestFetcher(t *testing.T) {
 			defer cancelF()
 			f := NewTestFetcher(ctrl)
 			test.PrepareFetcher(f)
-			segs, err := f.Fetcher().FetchSegs(ctx, test.Request)
+			segs, err := f.Fetcher().Fetch(ctx, test.Requests, false)
 			test.ErrorAssertion(t, err)
 			assert.Equal(t, test.ExpectedSegs, segs)
 		})
diff --git a/go/lib/infra/modules/segfetcher/requester_test.go b/go/lib/infra/modules/segfetcher/requester_test.go
index 1f132f66d..5f4c91be8 100644
--- a/go/lib/infra/modules/segfetcher/requester_test.go
+++ b/go/lib/infra/modules/segfetcher/requester_test.go
@@ -31,6 +31,12 @@ import (
 	"github.com/scionproto/scion/go/proto"
 )
 
+const (
+	Up   = proto.PathSegType_up
+	Down = proto.PathSegType_down
+	Core = proto.PathSegType_core
+)
+
 var (
 	isd1 = xtest.MustParseIA("1-0")
 	isd2 = xtest.MustParseIA("2-0")
@@ -45,15 +51,12 @@ var (
 	non_core_211 = xtest.MustParseIA("2-ff00:0:211")
 	non_core_212 = xtest.MustParseIA("2-ff00:0:212")
 
-	req_111_1   = segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetch}
-	req_1_111   = segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Fetch}
-	req_1_2     = segfetcher.Request{Src: isd1, Dst: isd2}
-	req_1_210   = segfetcher.Request{Src: isd1, Dst: core_210}
-	req_2_211   = segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Fetch}
-	req_210_1   = segfetcher.Request{Src: core_210, Dst: isd1}
-	req_210_110 = segfetcher.Request{Src: core_210, Dst: core_110, State: segfetcher.Fetch}
-	req_210_120 = segfetcher.Request{Src: core_210, Dst: core_120, State: segfetcher.Fetch}
-	req_210_130 = segfetcher.Request{Src: core_210, Dst: core_130, State: segfetcher.Fetch}
+	req_111_1   = segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1}
+	req_1_111   = segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111}
+	req_2_211   = segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211}
+	req_210_110 = segfetcher.Request{SegType: Core, Src: core_210, Dst: core_110}
+	req_210_120 = segfetcher.Request{SegType: Core, Src: core_210, Dst: core_120}
+	req_210_130 = segfetcher.Request{SegType: Core, Src: core_210, Dst: core_130}
 )
 
 func TestRequester(t *testing.T) {
@@ -62,25 +65,23 @@ func TestRequester(t *testing.T) {
 	tg := newTestGraph(rootCtrl)
 
 	tests := map[string]struct {
-		Req    segfetcher.RequestSet
+		Reqs   segfetcher.Requests
 		Expect func(*mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr
 	}{
 		"Empty req": {
-			Req: segfetcher.RequestSet{},
+			Reqs: segfetcher.Requests{},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				return nil
 			},
 		},
 		"Up only": {
-			Req: segfetcher.RequestSet{
-				Up: req_111_1,
-			},
+			Reqs: segfetcher.Requests{req_111_1},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req := req_111_1.ToSegReq()
 				reply := &path_mgmt.SegReply{
 					Req: req,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{{Type: proto.PathSegType_up, Segment: tg.seg120_111}},
+						Recs: []*seg.Meta{tg.seg120_111_up},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req), gomock.Any(), gomock.Any()).
@@ -89,18 +90,13 @@ func TestRequester(t *testing.T) {
 			},
 		},
 		"Down only": {
-			Req: segfetcher.RequestSet{
-				Down: req_1_111,
-			},
+			Reqs: segfetcher.Requests{req_1_111},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req := req_1_111.ToSegReq()
 				reply := &path_mgmt.SegReply{
 					Req: req,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{
-							{Type: proto.PathSegType_down, Segment: tg.seg120_111},
-							{Type: proto.PathSegType_down, Segment: tg.seg130_111},
-						},
+						Recs: []*seg.Meta{tg.seg120_111_down, tg.seg130_111_down},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req), gomock.Any(), gomock.Any()).
@@ -109,9 +105,7 @@ func TestRequester(t *testing.T) {
 			},
 		},
 		"Cores only": {
-			Req: segfetcher.RequestSet{
-				Cores: segfetcher.Requests{req_210_110, req_210_120, req_210_130},
-			},
+			Reqs: segfetcher.Requests{req_210_110, req_210_120, req_210_130},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req1 := req_210_110.ToSegReq()
 				testErr := errors.New("test error.")
@@ -121,7 +115,7 @@ func TestRequester(t *testing.T) {
 				reply2 := &path_mgmt.SegReply{
 					Req: req2,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{{Type: proto.PathSegType_core, Segment: tg.seg210_120}},
+						Recs: []*seg.Meta{tg.seg210_120_core},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req2), gomock.Any(), gomock.Any()).
@@ -130,7 +124,7 @@ func TestRequester(t *testing.T) {
 				reply3 := &path_mgmt.SegReply{
 					Req: req2,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{{Type: proto.PathSegType_core, Segment: tg.seg210_130}},
+						Recs: []*seg.Meta{tg.seg210_130_core},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req3), gomock.Any(), gomock.Any()).
@@ -143,16 +137,13 @@ func TestRequester(t *testing.T) {
 			},
 		},
 		"Up cores": {
-			Req: segfetcher.RequestSet{
-				Up:    req_111_1,
-				Cores: segfetcher.Requests{req_1_210},
-			},
+			Reqs: segfetcher.Requests{req_111_1},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req := req_111_1.ToSegReq()
 				reply := &path_mgmt.SegReply{
 					Req: req,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{{Type: proto.PathSegType_up, Segment: tg.seg120_111}},
+						Recs: []*seg.Meta{tg.seg120_111_up},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req), gomock.Any(), gomock.Any()).
@@ -161,19 +152,13 @@ func TestRequester(t *testing.T) {
 			},
 		},
 		"Cores down": {
-			Req: segfetcher.RequestSet{
-				Cores: segfetcher.Requests{req_210_1},
-				Down:  req_1_111,
-			},
+			Reqs: segfetcher.Requests{req_1_111},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req := req_1_111.ToSegReq()
 				reply := &path_mgmt.SegReply{
 					Req: req,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{
-							{Type: proto.PathSegType_down, Segment: tg.seg120_111},
-							{Type: proto.PathSegType_down, Segment: tg.seg130_111},
-						},
+						Recs: []*seg.Meta{tg.seg120_111_down, tg.seg130_111_down},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req), gomock.Any(), gomock.Any()).
@@ -182,20 +167,13 @@ func TestRequester(t *testing.T) {
 			},
 		},
 		"Up cores down": {
-			Req: segfetcher.RequestSet{
-				Up:    req_111_1,
-				Cores: segfetcher.Requests{req_1_2},
-				Down:  req_2_211,
-			},
+			Reqs: segfetcher.Requests{req_111_1, req_2_211},
 			Expect: func(api *mock_segfetcher.MockRequestAPI) []segfetcher.ReplyOrErr {
 				req1 := req_111_1.ToSegReq()
 				reply1 := &path_mgmt.SegReply{
 					Req: req1,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{
-							{Type: proto.PathSegType_up, Segment: tg.seg120_111},
-							{Type: proto.PathSegType_up, Segment: tg.seg130_111},
-						},
+						Recs: []*seg.Meta{tg.seg120_111_up, tg.seg130_111_up},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req1), gomock.Any(), gomock.Any()).
@@ -204,9 +182,7 @@ func TestRequester(t *testing.T) {
 				reply2 := &path_mgmt.SegReply{
 					Req: req2,
 					Recs: &path_mgmt.SegRecs{
-						Recs: []*seg.Meta{
-							{Type: proto.PathSegType_down, Segment: tg.seg210_211},
-						},
+						Recs: []*seg.Meta{tg.seg210_211_down},
 					},
 				}
 				api.EXPECT().GetSegs(gomock.Any(), gomock.Eq(req2), gomock.Any(), gomock.Any()).
@@ -234,7 +210,7 @@ func TestRequester(t *testing.T) {
 				DstProvider: dstProvider,
 			}
 			var replies []segfetcher.ReplyOrErr
-			for r := range requester.Request(ctx, test.Req) {
+			for r := range requester.Request(ctx, test.Reqs) {
 				replies = append(replies, r)
 			}
 			assert.ElementsMatch(t, expectedReplies, replies)
diff --git a/go/lib/infra/modules/segfetcher/resolver_test.go b/go/lib/infra/modules/segfetcher/resolver_test.go
index 10e362b0d..45b82b752 100644
--- a/go/lib/infra/modules/segfetcher/resolver_test.go
+++ b/go/lib/infra/modules/segfetcher/resolver_test.go
@@ -42,44 +42,64 @@ import (
 type testGraph struct {
 	g *graph.Graph
 
-	seg110_120 *seg.PathSegment
-	seg110_130 *seg.PathSegment
-	seg130_111 *seg.PathSegment
-	seg120_111 *seg.PathSegment
+	seg110_120_core *seg.Meta
+	seg110_130_core *seg.Meta
+	seg130_111_up   *seg.Meta
+	seg130_111_down *seg.Meta
+	seg120_111_up   *seg.Meta
+	seg120_111_down *seg.Meta
 
-	seg210_120   *seg.PathSegment
-	seg210_130   *seg.PathSegment
-	seg210_130_2 *seg.PathSegment
-	seg210_211   *seg.PathSegment
-	seg210_212   *seg.PathSegment
+	seg210_120_core   *seg.Meta
+	seg210_130_core   *seg.Meta
+	seg210_130_2_core *seg.Meta
+	seg210_211_up     *seg.Meta
+	seg210_211_down   *seg.Meta
+	seg210_212_up     *seg.Meta
+	seg210_212_down   *seg.Meta
 }
 
 func newTestGraph(ctrl *gomock.Controller) *testGraph {
 	g := graph.NewDefaultGraph(ctrl)
 
+	seg110_120 := g.Beacon([]common.IFIDType{graph.If_110_X_120_A})
+	seg110_130 := g.Beacon([]common.IFIDType{graph.If_110_X_130_A})
+	seg120_111 := g.Beacon([]common.IFIDType{graph.If_120_X_111_B})
+	seg130_111 := g.Beacon([]common.IFIDType{graph.If_130_B_111_A})
+
+	seg210_120 := g.Beacon([]common.IFIDType{graph.If_210_X_110_X, graph.If_110_X_120_A})
+	seg210_130 := g.Beacon([]common.IFIDType{graph.If_210_X_110_X, graph.If_110_X_130_A})
+	seg210_130_2 := g.Beacon([]common.IFIDType{graph.If_210_X_220_X,
+		graph.If_220_X_120_B, graph.If_120_A_130_B})
+	seg210_211 := g.Beacon([]common.IFIDType{graph.If_210_X_211_A})
+	seg210_212 := g.Beacon([]common.IFIDType{graph.If_210_X_211_A, graph.If_211_A_212_X})
+
 	return &testGraph{
-		g:          g,
-		seg110_120: g.Beacon([]common.IFIDType{graph.If_110_X_120_A}),
-		seg110_130: g.Beacon([]common.IFIDType{graph.If_110_X_130_A}),
-		seg120_111: g.Beacon([]common.IFIDType{graph.If_120_X_111_B}),
-		seg130_111: g.Beacon([]common.IFIDType{graph.If_130_B_111_A}),
+		g:               g,
+		seg110_120_core: &seg.Meta{Type: Core, Segment: seg110_120},
+		seg110_130_core: &seg.Meta{Type: Core, Segment: seg110_130},
+		seg130_111_up:   &seg.Meta{Type: Up, Segment: seg130_111},
+		seg130_111_down: &seg.Meta{Type: Down, Segment: seg130_111},
+		seg120_111_up:   &seg.Meta{Type: Up, Segment: seg120_111},
+		seg120_111_down: &seg.Meta{Type: Down, Segment: seg120_111},
 
-		seg210_120: g.Beacon([]common.IFIDType{graph.If_210_X_110_X, graph.If_110_X_120_A}),
-		seg210_130: g.Beacon([]common.IFIDType{graph.If_210_X_110_X, graph.If_110_X_130_A}),
-		seg210_130_2: g.Beacon([]common.IFIDType{graph.If_210_X_220_X,
-			graph.If_220_X_120_B, graph.If_120_A_130_B}),
-		seg210_211: g.Beacon([]common.IFIDType{graph.If_210_X_211_A}),
-		seg210_212: g.Beacon([]common.IFIDType{graph.If_210_X_211_A, graph.If_211_A_212_X}),
+		seg210_120_core:   &seg.Meta{Type: Core, Segment: seg210_120},
+		seg210_130_core:   &seg.Meta{Type: Core, Segment: seg210_130},
+		seg210_130_2_core: &seg.Meta{Type: Core, Segment: seg210_130_2},
+		seg210_211_up:     &seg.Meta{Type: Up, Segment: seg210_211},
+		seg210_211_down:   &seg.Meta{Type: Down, Segment: seg210_211},
+		seg210_212_up:     &seg.Meta{Type: Up, Segment: seg210_212},
+		seg210_212_down:   &seg.Meta{Type: Down, Segment: seg210_212},
 	}
 }
 
 type resolverTest struct {
-	Req              segfetcher.RequestSet
-	Segs             segfetcher.Segments
-	ExpectCalls      func(db *mock_pathdb.MockPathDB)
-	ExpectRevcache   func(t *testing.T, revCache *mock_revcache.MockRevCache)
-	ExpectedSegments segfetcher.Segments
-	ExpectedReqSet   segfetcher.RequestSet
+	Reqs              segfetcher.Requests
+	Refresh           bool
+	Segs              segfetcher.Segments
+	ExpectCalls       func(db *mock_pathdb.MockPathDB)
+	ExpectRevcache    func(t *testing.T, revCache *mock_revcache.MockRevCache)
+	ExpectedSegments  segfetcher.Segments
+	ExpectedFetchReqs segfetcher.Requests
 }
 
 func (rt resolverTest) run(t *testing.T) {
@@ -95,9 +115,9 @@ func (rt resolverTest) run(t *testing.T) {
 		revCache.EXPECT().Get(gomock.Any(), gomock.Any()).AnyTimes()
 	}
 	resolver := segfetcher.NewResolver(db, revCache, neverLocal{})
-	segs, remainingReqs, err := resolver.Resolve(context.Background(), rt.Segs, rt.Req)
+	segs, fetchReqs, err := resolver.Resolve(context.Background(), rt.Reqs, rt.Refresh)
 	assert.Equal(t, rt.ExpectedSegments, segs)
-	assert.Equal(t, rt.ExpectedReqSet, remainingReqs)
+	assert.Equal(t, rt.ExpectedFetchReqs, fetchReqs)
 	assert.NoError(t, err)
 }
 
@@ -109,21 +129,21 @@ func TestResolver(t *testing.T) {
 
 	tests := map[string]resolverTest{
 		"Up wildcard": {
-			Req: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// no cached up segments
 				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_111),
 					gomock.Eq(isd1), gomock.Any())
 			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetch},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 		},
 		"Up wildcard (cached)": {
-			Req: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
@@ -132,53 +152,37 @@ func TestResolver(t *testing.T) {
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up: seg.Segments{tg.seg120_111, tg.seg130_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-			},
-		},
-		"Up wildcard (fetched)": {
-			Req: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetched},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_up},
-					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
+				})).Return(resultsFromSegs(tg.seg120_111_up, tg.seg130_111_up), nil)
 			},
 			ExpectedSegments: segfetcher.Segments{
-				Up: seg.Segments{tg.seg120_111, tg.seg130_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
+				tg.seg120_111_up,
+				tg.seg130_111_up,
 			},
+			ExpectedFetchReqs: nil,
 		},
 		"Up Core": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: core_110}},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: core_110},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// no cached up segments
 				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_111),
 					gomock.Eq(isd1), gomock.Any())
+				// no cached core segments
+				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd1),
+					gomock.Eq(core_110), gomock.Any())
 			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetch},
-				Cores: []segfetcher.Request{
-					{Src: isd1, Dst: core_110, State: segfetcher.Unresolved},
-				},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: core_110},
 			},
 		},
 		"Up(cached) Core": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: core_110}},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: core_120, Dst: core_110},
+				segfetcher.Request{SegType: Core, Src: core_130, Dst: core_110},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
@@ -187,26 +191,24 @@ func TestResolver(t *testing.T) {
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
+				})).Return(resultsFromSegs(tg.seg120_111_up, tg.seg130_111_up), nil)
 				// no cached core segments
 				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_120),
 					gomock.Eq(core_110), gomock.Any())
 				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_130),
 					gomock.Eq(core_110), gomock.Any())
 			},
-			ExpectedSegments: segfetcher.Segments{Up: seg.Segments{tg.seg120_111, tg.seg130_111}},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_120, Dst: core_110, State: segfetcher.Fetch},
-					{Src: core_130, Dst: core_110, State: segfetcher.Fetch},
-				},
+			ExpectedSegments: segfetcher.Segments{tg.seg120_111_up, tg.seg130_111_up},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_120, Dst: core_110},
+				segfetcher.Request{SegType: Core, Src: core_130, Dst: core_110},
 			},
 		},
 		"Up(cached) Core(cached)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: core_110}},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: core_120, Dst: core_110},
+				segfetcher.Request{SegType: Core, Src: core_130, Dst: core_110},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
@@ -215,7 +217,7 @@ func TestResolver(t *testing.T) {
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
+				})).Return(resultsFromSegs(tg.seg120_111_up, tg.seg130_111_up), nil)
 				// cached core segments
 				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_120),
 					gomock.Eq(core_110), gomock.Any()).Return(futureT, nil)
@@ -224,467 +226,129 @@ func TestResolver(t *testing.T) {
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_core},
 					StartsAt: []addr.IA{core_110}, EndsAt: []addr.IA{core_120},
-				})).Return(resultsFromSegs(tg.seg110_120), nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_110}, EndsAt: []addr.IA{core_130},
-				})).Return(resultsFromSegs(tg.seg110_130), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg120_111, tg.seg130_111},
-				Core: seg.Segments{tg.seg110_120, tg.seg110_130},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_120, Dst: core_110, State: segfetcher.Loaded},
-					{Src: core_130, Dst: core_110, State: segfetcher.Loaded},
-				},
-			},
-		},
-		"Up(fetched) Core(fetched)": {
-			Req: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetched},
-				Cores: []segfetcher.Request{
-					{Src: core_120, Dst: core_110, State: segfetcher.Fetched},
-					{Src: core_130, Dst: core_110, State: segfetcher.Fetched},
-				},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_up},
-					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_110}, EndsAt: []addr.IA{core_120},
-				})).Return(resultsFromSegs(tg.seg110_120), nil)
+				})).Return(resultsFromSegs(tg.seg110_120_core), nil)
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_core},
 					StartsAt: []addr.IA{core_110}, EndsAt: []addr.IA{core_130},
-				})).Return(resultsFromSegs(tg.seg110_130), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg120_111, tg.seg130_111},
-				Core: seg.Segments{tg.seg110_120, tg.seg110_130},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_120, Dst: core_110, State: segfetcher.Loaded},
-					{Src: core_130, Dst: core_110, State: segfetcher.Loaded},
-				},
-			},
-		},
-		"Up(passed) Core": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{{Src: core_120, Dst: core_110}},
-			},
-			Segs: segfetcher.Segments{
-				Up: seg.Segments{tg.seg120_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_120),
-					gomock.Eq(core_110), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up: seg.Segments{tg.seg120_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_120, Dst: core_110, State: segfetcher.Fetch},
-				},
+				})).Return(resultsFromSegs(tg.seg110_130_core), nil)
 			},
+			ExpectedSegments: segfetcher.Segments{tg.seg120_111_up, tg.seg130_111_up,
+				tg.seg110_120_core, tg.seg110_130_core},
+			ExpectedFetchReqs: nil,
 		},
 		"Up down": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_212},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_212},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				db.EXPECT().GetNextQuery(gomock.Any(), non_core_211, isd2, gomock.Any())
+				db.EXPECT().GetNextQuery(gomock.Any(), isd2, isd2, gomock.Any())
 				db.EXPECT().GetNextQuery(gomock.Any(), isd2, non_core_212, gomock.Any())
 			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Fetch},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_212, State: segfetcher.Fetch},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_212},
 			},
 		},
 		"Up(cached) down(cached)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_212},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_212},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				db.EXPECT().GetNextQuery(gomock.Any(), non_core_211, isd2, gomock.Any()).
 					Return(futureT, nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), isd2, isd2, gomock.Any()).
+					Return(futureT, nil)
 				db.EXPECT().GetNextQuery(gomock.Any(), isd2, non_core_212, gomock.Any()).
 					Return(futureT, nil)
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_211},
-				})).Return(resultsFromSegs(tg.seg210_211), nil)
+				})).Return(resultsFromSegs(tg.seg210_211_up), nil)
+				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
+					SegTypes: []proto.PathSegType{proto.PathSegType_core},
+					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{isd2},
+				}))
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_down},
 					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_212},
-				})).Return(resultsFromSegs(tg.seg210_212), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg210_211},
-				Down: seg.Segments{tg.seg210_212},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up:   segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Down: segfetcher.Request{Src: isd2, Dst: non_core_212, State: segfetcher.Loaded},
+				})).Return(resultsFromSegs(tg.seg210_212_down), nil)
 			},
+			ExpectedSegments:  segfetcher.Segments{tg.seg210_211_up, tg.seg210_212_down},
+			ExpectedFetchReqs: nil,
 		},
 		"Up Core Down": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				// no cached up segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_111),
-					gomock.Eq(isd1), gomock.Any())
-				// no cached down segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd2),
-					gomock.Eq(non_core_211), gomock.Any())
+				// no cached segments
+				db.EXPECT().GetNextQuery(gomock.Any(), non_core_111, isd1, gomock.Any())
+				db.EXPECT().GetNextQuery(gomock.Any(), isd1, isd2, gomock.Any())
+				db.EXPECT().GetNextQuery(gomock.Any(), isd2, non_core_211, gomock.Any())
 			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetch},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Fetch},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 		},
 		"Up(cached) Core Down": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_111),
-					gomock.Eq(isd1), gomock.Any()).Return(futureT, nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), non_core_111, isd1, gomock.Any()).
+					Return(futureT, nil)
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd2),
-					gomock.Eq(non_core_211), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up: seg.Segments{tg.seg120_111, tg.seg130_111},
+				})).Return(resultsFromSegs(tg.seg120_111_up, tg.seg130_111_up), nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), isd1, isd2, gomock.Any())
+				db.EXPECT().GetNextQuery(gomock.Any(), isd2, non_core_211, gomock.Any())
 			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Fetch},
+			ExpectedSegments: segfetcher.Segments{tg.seg120_111_up, tg.seg130_111_up},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 		},
 		"Up(cached) Core Down(cached)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd1},
+				segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_211),
-					gomock.Eq(isd2), gomock.Any()).
-					Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_up},
-					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_211},
-				})).Return(resultsFromSegs(tg.seg210_211), nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd1),
-					gomock.Eq(non_core_111), gomock.Any()).
-					Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_down},
-					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any())
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_130), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg210_211},
-				Down: seg.Segments{tg.seg120_111, tg.seg130_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_120, State: segfetcher.Fetch},
-					{Src: core_210, Dst: core_130, State: segfetcher.Fetch},
-				},
-				Down: segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-			},
-		},
-		"Up (0 cached) Core Down(cached)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(non_core_211),
-					gomock.Eq(isd2), gomock.Any()).
+				db.EXPECT().GetNextQuery(gomock.Any(), non_core_211, isd2, gomock.Any()).
 					Return(futureT, nil)
-				// return no up segments
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_211},
-				}))
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd1),
-					gomock.Eq(non_core_111), gomock.Any()).
+				})).Return(resultsFromSegs(tg.seg210_211_up), nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), isd1, non_core_111, gomock.Any()).
 					Return(futureT, nil)
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_down},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{},
-				Down: seg.Segments{tg.seg120_111, tg.seg130_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: isd2, Dst: isd1, State: segfetcher.Loaded},
-				},
-				Down: segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-			},
-		},
-		"Up(passed) Core Down(passed)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-			},
-			Segs: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg210_211},
-				Down: seg.Segments{tg.seg120_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg210_211},
-				Down: seg.Segments{tg.seg120_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_120, State: segfetcher.Fetch},
-				},
-				Down: segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-			},
-		},
-		"Core(partial cached)": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110},
-					{Src: core_210, Dst: core_120},
-					{Src: core_210, Dst: core_130},
-				},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_110), gomock.Any())
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any())
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_130), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_130}, EndsAt: []addr.IA{core_210},
-				})).Return(resultsFromSegs(tg.seg210_130), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Core: seg.Segments{tg.seg210_130},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110, State: segfetcher.Fetch},
-					{Src: core_210, Dst: core_120, State: segfetcher.Fetch},
-					{Src: core_210, Dst: core_130, State: segfetcher.Loaded},
-				},
-			},
-		},
-		"Core(cached)": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110},
-					{Src: core_210, Dst: core_120},
-					{Src: core_210, Dst: core_130},
-				},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_110), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_130), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_130}, EndsAt: []addr.IA{core_210},
-				})).Return(resultsFromSegs(tg.seg210_130), nil)
-				// no result cached for the others
-				db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(2)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Core: seg.Segments{tg.seg210_130},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110, State: segfetcher.Loaded},
-					{Src: core_210, Dst: core_120, State: segfetcher.Loaded},
-					{Src: core_210, Dst: core_130, State: segfetcher.Loaded},
-				},
-			},
-		},
-		"Core Down": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd2),
-					gomock.Eq(non_core_211), gomock.Any())
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Fetch},
-			},
-		},
-		"Core Down(cached)": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd2),
-					gomock.Eq(non_core_211), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_down},
-					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_211},
-				})).Return(resultsFromSegs(tg.seg210_211), nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_110),
-					gomock.Eq(core_210), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Down: seg.Segments{tg.seg210_211},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_110, Dst: core_210, State: segfetcher.Fetch},
-				},
-				Down: segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Loaded},
-			},
-		},
-		"Core Down(passed)": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: core_210}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Loaded},
-			},
-			Segs: segfetcher.Segments{
-				Down: seg.Segments{tg.seg210_211},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_110),
-					gomock.Eq(core_210), gomock.Any())
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Down: seg.Segments{tg.seg210_211},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_110, Dst: core_210, State: segfetcher.Fetch},
-				},
-				Down: segfetcher.Request{Src: isd2, Dst: non_core_211, State: segfetcher.Loaded},
-			},
-		},
-		"Core(cached) Down(cached)": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_210, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(isd1),
-					gomock.Eq(non_core_111), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_down},
-					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_130), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_120}, EndsAt: []addr.IA{core_210},
-				})).Return(resultsFromSegs(tg.seg210_120), nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_core},
-					StartsAt: []addr.IA{core_130}, EndsAt: []addr.IA{core_210},
-				})).Return(resultsFromSegs(tg.seg210_130), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Down: seg.Segments{tg.seg120_111, tg.seg130_111},
-				Core: seg.Segments{tg.seg210_120, tg.seg210_130},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_120, State: segfetcher.Loaded},
-					{Src: core_210, Dst: core_130, State: segfetcher.Loaded},
-				},
-				Down: segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-			},
-		},
-		"Down": {
-			Req: segfetcher.RequestSet{
-				Down: segfetcher.Request{Src: core_120, Dst: non_core_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				// no cached up segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_120),
-					gomock.Eq(non_core_111), gomock.Any())
-
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Down: segfetcher.Request{Src: core_120, Dst: non_core_111, State: segfetcher.Fetch},
-			},
-		},
-		"Down(cached)": {
-			Req: segfetcher.RequestSet{
-				Down: segfetcher.Request{Src: core_120, Dst: non_core_111},
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				// no cached up segments
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_120),
-					gomock.Eq(non_core_111), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_down},
-					StartsAt: []addr.IA{core_120}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111), nil)
+				})).Return(resultsFromSegs(tg.seg120_111_down, tg.seg130_111_down), nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), isd2, isd1, gomock.Any())
 			},
-			ExpectedSegments: segfetcher.Segments{
-				Down: seg.Segments{tg.seg120_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Down: segfetcher.Request{Src: core_120, Dst: non_core_111,
-					State: segfetcher.Loaded},
+			ExpectedSegments: segfetcher.Segments{tg.seg210_211_up,
+				tg.seg120_111_down, tg.seg130_111_down},
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd1},
 			},
 		},
 	}
@@ -696,56 +360,23 @@ func TestResolver(t *testing.T) {
 func TestResolverCacheBypass(t *testing.T) {
 	rootCtrl := gomock.NewController(t)
 	defer rootCtrl.Finish()
-	tg := newTestGraph(rootCtrl)
+	// tg := newTestGraph(rootCtrl)
 	// futureT := time.Now().Add(2 * time.Minute)
 
 	tests := map[string]resolverTest{
 		"Up(cache-bypass) Core Down(cache-bypass)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111},
-				Fetch: true,
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd1},
+				segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111},
 			},
+			Refresh:          true,
 			ExpectCalls:      func(db *mock_pathdb.MockPathDB) {},
-			ExpectedSegments: segfetcher.Segments{},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Fetch},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Fetch},
-				Fetch: true,
-			},
-		},
-		"Up(fetched) Core Down(fetched)": {
-			Req: segfetcher.RequestSet{
-				Up:    segfetcher.Request{State: segfetcher.Fetched, Src: non_core_211, Dst: isd2},
-				Cores: []segfetcher.Request{{Src: isd2, Dst: isd1}},
-				Down:  segfetcher.Request{State: segfetcher.Fetched, Src: isd1, Dst: non_core_111},
-				Fetch: true,
-			},
-			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				// cached up segments
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_up},
-					StartsAt: []addr.IA{isd2}, EndsAt: []addr.IA{non_core_211},
-				})).Return(resultsFromSegs(tg.seg210_211), nil)
-				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
-					SegTypes: []proto.PathSegType{proto.PathSegType_down},
-					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
-			},
-			ExpectedSegments: segfetcher.Segments{
-				Up:   seg.Segments{tg.seg210_211},
-				Down: seg.Segments{tg.seg120_111, tg.seg130_111},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_211, Dst: isd2, State: segfetcher.Loaded},
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_120, State: segfetcher.Fetch},
-					{Src: core_210, Dst: core_130, State: segfetcher.Fetch},
-				},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111, State: segfetcher.Loaded},
-				Fetch: true,
+			ExpectedSegments: nil,
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_211, Dst: isd2},
+				segfetcher.Request{SegType: Core, Src: isd2, Dst: isd1},
+				segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111},
 			},
 		},
 	}
@@ -769,8 +400,8 @@ func TestResolverWithRevocations(t *testing.T) {
 	}
 	tests := map[string]resolverTest{
 		"Up wildcard (cached)": {
-			Req: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
 				// cached up segments
@@ -779,7 +410,7 @@ func TestResolverWithRevocations(t *testing.T) {
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_up},
 					StartsAt: []addr.IA{isd1}, EndsAt: []addr.IA{non_core_111},
-				})).Return(resultsFromSegs(tg.seg120_111, tg.seg130_111), nil)
+				})).Return(resultsFromSegs(tg.seg120_111_up, tg.seg130_111_up), nil)
 			},
 			ExpectRevcache: func(t *testing.T, revCache *mock_revcache.MockRevCache) {
 				key111_120 := revcache.Key{IA: non_core_111, IfId: graph.If_111_B_120_X}
@@ -790,30 +421,28 @@ func TestResolverWithRevocations(t *testing.T) {
 			},
 			// On the initial fetch, if everything is revoked, just try again
 			// and fetch it.
-			ExpectedSegments: segfetcher.Segments{},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1, State: segfetcher.Fetch},
+			ExpectedSegments: nil,
+			ExpectedFetchReqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 		},
 		"Core (cached) with revocations returns full result": {
-			Req: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110},
-					{Src: core_210, Dst: core_120},
-					{Src: core_210, Dst: core_130},
-				},
+			Reqs: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_210, Dst: core_110},
+				segfetcher.Request{SegType: Core, Src: core_210, Dst: core_120},
+				segfetcher.Request{SegType: Core, Src: core_210, Dst: core_130},
 			},
 			ExpectCalls: func(db *mock_pathdb.MockPathDB) {
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_110), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_120), gomock.Any()).Return(futureT, nil)
-				db.EXPECT().GetNextQuery(gomock.Any(), gomock.Eq(core_210),
-					gomock.Eq(core_130), gomock.Any()).Return(futureT, nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), core_210, core_110, gomock.Any()).
+					Return(futureT, nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), core_210, core_120, gomock.Any()).
+					Return(futureT, nil)
+				db.EXPECT().GetNextQuery(gomock.Any(), core_210, core_130, gomock.Any()).
+					Return(futureT, nil)
 				db.EXPECT().Get(gomock.Any(), matchers.EqParams(&query.Params{
 					SegTypes: []proto.PathSegType{proto.PathSegType_core},
 					StartsAt: []addr.IA{core_130}, EndsAt: []addr.IA{core_210},
-				})).Return(resultsFromSegs(tg.seg210_130, tg.seg210_130_2), nil)
+				})).Return(resultsFromSegs(tg.seg210_130_core, tg.seg210_130_2_core), nil)
 				// Other calls return 0
 				db.EXPECT().Get(gomock.Any(), gomock.Any()).Times(2)
 			},
@@ -827,16 +456,8 @@ func TestResolverWithRevocations(t *testing.T) {
 				}, nil)
 				revCache.EXPECT().Get(gomock.Any(), gomock.Any()).AnyTimes()
 			},
-			ExpectedSegments: segfetcher.Segments{
-				Core: seg.Segments{tg.seg210_130, tg.seg210_130_2},
-			},
-			ExpectedReqSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{
-					{Src: core_210, Dst: core_110, State: segfetcher.Loaded},
-					{Src: core_210, Dst: core_120, State: segfetcher.Loaded},
-					{Src: core_210, Dst: core_130, State: segfetcher.Loaded},
-				},
-			},
+			ExpectedSegments:  segfetcher.Segments{tg.seg210_130_core, tg.seg210_130_2_core},
+			ExpectedFetchReqs: nil,
 		},
 	}
 	for name, test := range tests {
@@ -844,11 +465,12 @@ func TestResolverWithRevocations(t *testing.T) {
 	}
 }
 
-func resultsFromSegs(segs ...*seg.PathSegment) query.Results {
+func resultsFromSegs(segs ...*seg.Meta) query.Results {
 	results := make(query.Results, 0, len(segs))
 	for _, seg := range segs {
 		results = append(results, &query.Result{
-			Seg:        seg,
+			Type:       seg.Type,
+			Seg:        seg.Segment,
 			LastUpdate: time.Now().Add(-2 * time.Minute),
 		})
 	}
@@ -878,4 +500,6 @@ func (m keySetContains) String() string {
 
 type neverLocal struct{}
 
-func (neverLocal) IsSegLocal(_ context.Context, _, _ addr.IA) (bool, error) { return false, nil }
+func (neverLocal) IsSegLocal(_ segfetcher.Request) bool {
+	return false
+}
diff --git a/go/lib/infra/modules/segfetcher/splitter_test.go b/go/lib/infra/modules/segfetcher/splitter_test.go
index 253839b66..4ff7da906 100644
--- a/go/lib/infra/modules/segfetcher/splitter_test.go
+++ b/go/lib/infra/modules/segfetcher/splitter_test.go
@@ -49,118 +49,106 @@ func TestRequestSplitter(t *testing.T) {
 	).AnyTimes()
 	tests := map[string]struct {
 		LocalIA        addr.IA
-		Request        segfetcher.Request
-		ExpectedSet    segfetcher.RequestSet
+		Dst            addr.IA
+		ExpectedSet    segfetcher.Requests
 		ExpectedErrMsg string
 	}{
 		"Up": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: core_110},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: core_110}},
+			Dst:     core_110,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: core_110},
 			},
 		},
 		"Up wildcard": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: isd1},
-			ExpectedSet: segfetcher.RequestSet{
-				Up: segfetcher.Request{Src: non_core_111, Dst: isd1},
+			Dst:     isd1,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
 			},
 		},
 		"Up core non-local": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: core_210},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: core_210}},
+			Dst:     core_210,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: core_210},
 			},
 		},
 		"Up Core non-local wildcard": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: isd2},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
+			Dst:     isd2,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
 			},
 		},
 		"Down local": {
 			LocalIA: core_110,
-			Request: segfetcher.Request{Dst: non_core_111},
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_111},
+			Dst:     non_core_111,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_110, Dst: isd1},
+				segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_111},
 			},
 		},
 		"Down non-local": {
 			LocalIA: core_110,
-			Request: segfetcher.Request{Dst: non_core_211},
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
+			Dst:     non_core_211,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 		},
 		"Core local": {
 			LocalIA: core_110,
-			Request: segfetcher.Request{Dst: core_130},
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: core_130}},
+			Dst:     core_130,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_110, Dst: core_130},
 			},
 		},
 		"Core non-local": {
 			LocalIA: core_110,
-			Request: segfetcher.Request{Dst: core_210},
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: core_210}},
+			Dst:     core_210,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_110, Dst: core_210},
 			},
 		},
 		"Core non-local wildcard": {
 			LocalIA: core_110,
-			Request: segfetcher.Request{Dst: isd2},
-			ExpectedSet: segfetcher.RequestSet{
-				Cores: []segfetcher.Request{{Src: core_110, Dst: isd2}},
+			Dst:     isd2,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Core, Src: core_110, Dst: isd2},
 			},
 		},
 		"Up down local": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: non_core_112},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd1}},
-				Down:  segfetcher.Request{Src: isd1, Dst: non_core_112},
+			Dst:     non_core_112,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd1},
+				segfetcher.Request{SegType: Down, Src: isd1, Dst: non_core_112},
 			},
 		},
 		"Up down non-local": {
 			LocalIA: non_core_111,
-			Request: segfetcher.Request{Src: non_core_111, Dst: non_core_211},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
-			},
-		},
-		"Up down non-local passes state": {
-			LocalIA: non_core_111,
-			Request: segfetcher.Request{
-				State: segfetcher.Fetch,
-				Src:   non_core_111,
-				Dst:   non_core_211,
-			},
-			ExpectedSet: segfetcher.RequestSet{
-				Up:    segfetcher.Request{Src: non_core_111, Dst: isd1},
-				Cores: []segfetcher.Request{{Src: isd1, Dst: isd2}},
-				Down:  segfetcher.Request{Src: isd2, Dst: non_core_211},
-				Fetch: true,
+			Dst:     non_core_211,
+			ExpectedSet: segfetcher.Requests{
+				segfetcher.Request{SegType: Up, Src: non_core_111, Dst: isd1},
+				segfetcher.Request{SegType: Core, Src: isd1, Dst: isd2},
+				segfetcher.Request{SegType: Down, Src: isd2, Dst: non_core_211},
 			},
 		},
 	}
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
+			_, core := cores[test.LocalIA]
 			splitter := segfetcher.MultiSegmentSplitter{
-				Local:     test.LocalIA,
+				LocalIA:   test.LocalIA,
 				Inspector: inspector,
+				Core:      core,
 			}
-			requests, err := splitter.Split(context.Background(), test.Request)
+			requests, err := splitter.Split(context.Background(), test.Dst)
 			if test.ExpectedErrMsg != "" {
 				assert.Error(t, err)
 				assert.Contains(t, err.Error(), test.ExpectedErrMsg)