Repository: scionproto/scion
Pull request #3433 (2019-11-27 14:04:32)

snet: Add PathFingerprint type


<!-- 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/3433)
<!-- Reviewable:end -->



Patch:
diff --git a/go/lib/pathpol/BUILD.bazel b/go/lib/pathpol/BUILD.bazel
index d2be144d0..90902cafc 100644
--- a/go/lib/pathpol/BUILD.bazel
+++ b/go/lib/pathpol/BUILD.bazel
@@ -17,6 +17,7 @@ go_library(
         "//go/lib/log:go_default_library",
         "//go/lib/pathpol/sequence:go_default_library",
         "//go/lib/serrors:go_default_library",
+        "//go/lib/snet:go_default_library",
         "@com_github_antlr_antlr4//runtime/Go/antlr:go_default_library",
     ],
 )
@@ -33,6 +34,7 @@ go_test(
     deps = [
         "//go/lib/addr:go_default_library",
         "//go/lib/common:go_default_library",
+        "//go/lib/snet:go_default_library",
         "//go/lib/xtest:go_default_library",
         "//go/lib/xtest/graph:go_default_library",
         "@com_github_golang_mock//gomock:go_default_library",
diff --git a/go/lib/pathpol/pathset.go b/go/lib/pathpol/pathset.go
index 2dc66f539..9978971c2 100644
--- a/go/lib/pathpol/pathset.go
+++ b/go/lib/pathpol/pathset.go
@@ -17,17 +17,18 @@ package pathpol
 import (
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/common"
+	"github.com/scionproto/scion/go/lib/snet"
 )
 
 // PathSet is a set of paths. PathSet is used for policy filtering.
-type PathSet map[string]Path
+type PathSet map[snet.PathFingerprint]Path
 
 // Path describes a path or a partial path, e.g. a segment.
 type Path interface {
 	// Interfaces returns all the interfaces of this path.
 	Interfaces() []PathInterface
 	// Returns a string that uniquely identifies this path.
-	Key() string
+	Key() snet.PathFingerprint
 }
 
 // PathInterface is an interface on the path.
diff --git a/go/lib/sciond/apitypes.go b/go/lib/sciond/apitypes.go
index dbc9e4b2e..813802df4 100644
--- a/go/lib/sciond/apitypes.go
+++ b/go/lib/sciond/apitypes.go
@@ -135,7 +135,7 @@ func pathReplyEntryToPath(pe PathReplyEntry, dst addr.IA) (Path, error) {
 	return p, nil
 }
 
-func (p Path) Fingerprint() string {
+func (p Path) Fingerprint() snet.PathFingerprint {
 	if len(p.interfaces) == 0 {
 		return ""
 	}
@@ -144,7 +144,7 @@ func (p Path) Fingerprint() string {
 		binary.Write(h, common.Order, intf.IA().IAInt())
 		binary.Write(h, common.Order, intf.ID())
 	}
-	return string(h.Sum(nil))
+	return snet.PathFingerprint(h.Sum(nil))
 }
 
 func (p Path) OverlayNextHop() *net.UDPAddr {
diff --git a/go/lib/snet/mock_snet/snet.go b/go/lib/snet/mock_snet/snet.go
index 8b5312276..5c3abab5e 100644
--- a/go/lib/snet/mock_snet/snet.go
+++ b/go/lib/snet/mock_snet/snet.go
@@ -507,10 +507,10 @@ func (mr *MockPathMockRecorder) Expiry() *gomock.Call {
 }
 
 // Fingerprint mocks base method
-func (m *MockPath) Fingerprint() string {
+func (m *MockPath) Fingerprint() snet.PathFingerprint {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Fingerprint")
-	ret0, _ := ret[0].(string)
+	ret0, _ := ret[0].(snet.PathFingerprint)
 	return ret0
 }
 
diff --git a/go/lib/snet/path.go b/go/lib/snet/path.go
index 352c6fe8d..216fd3794 100644
--- a/go/lib/snet/path.go
+++ b/go/lib/snet/path.go
@@ -23,6 +23,12 @@ import (
 	"github.com/scionproto/scion/go/lib/spath"
 )
 
+type PathFingerprint string
+
+func (pf PathFingerprint) String() string {
+	return common.RawBytes(pf).String()
+}
+
 // Path is an abstract representation of a path. Most applications do not need
 // access to the raw internals.
 //
@@ -35,7 +41,7 @@ type Path interface {
 	// Fingerprint uniquely identifies the path based on the sequence of
 	// ASes and BRs. Other metadata, such as MTU or NextHop have no effect
 	// on the fingerprint. Empty string means unknown fingerprint.
-	Fingerprint() string
+	Fingerprint() PathFingerprint
 	// OverlayNextHop returns the address:port pair of a local-AS overlay
 	// speaker. Usually, this is a border router that will forward the traffic.
 	OverlayNextHop() *net.UDPAddr
@@ -75,7 +81,7 @@ type partialPath struct {
 	destination addr.IA
 }
 
-func (p *partialPath) Fingerprint() string {
+func (p *partialPath) Fingerprint() PathFingerprint {
 	return ""
 }
 
diff --git a/go/lib/spath/spathmeta/apppath.go b/go/lib/spath/spathmeta/apppath.go
index ff03482f2..434a8788a 100644
--- a/go/lib/spath/spathmeta/apppath.go
+++ b/go/lib/spath/spathmeta/apppath.go
@@ -24,7 +24,7 @@ import (
 )
 
 // AppPathSet represents a set of SCIOND path entries, keyed by AppPath.Key().
-type AppPathSet map[string]snet.Path
+type AppPathSet map[snet.PathFingerprint]snet.Path
 
 // NewAppPathSet creates a new set of paths from a SCIOND path reply.
 func NewAppPathSet(paths []snet.Path) AppPathSet {
@@ -54,7 +54,7 @@ func (aps AppPathSet) Copy() AppPathSet {
 // GetAppPath returns an AppPath from the set. It first tries to find
 // a path with key pref; if one cannot be found, an arbitrary one
 // is returned.
-func (aps AppPathSet) GetAppPath(pref string) snet.Path {
+func (aps AppPathSet) GetAppPath(pref snet.PathFingerprint) snet.Path {
 	if len(pref) > 0 {
 		ap, ok := aps[pref]
 		if ok {
diff --git a/go/lib/svc/resolver.go b/go/lib/svc/resolver.go
index 5083c0d5a..85237ae26 100644
--- a/go/lib/svc/resolver.go
+++ b/go/lib/svc/resolver.go
@@ -170,7 +170,7 @@ type path struct {
 	destination addr.IA
 }
 
-func (p *path) Fingerprint() string {
+func (p *path) Fingerprint() snet.PathFingerprint {
 	return ""
 }
 
diff --git a/go/path_srv/internal/segutil/BUILD.bazel b/go/path_srv/internal/segutil/BUILD.bazel
index 5a9adbbae..bb1749e46 100644
--- a/go/path_srv/internal/segutil/BUILD.bazel
+++ b/go/path_srv/internal/segutil/BUILD.bazel
@@ -10,6 +10,7 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/ctrl/seg:go_default_library",
         "//go/lib/pathpol:go_default_library",
+        "//go/lib/snet:go_default_library",
     ],
 )
 
diff --git a/go/path_srv/internal/segutil/filter.go b/go/path_srv/internal/segutil/filter.go
index 6a5c30919..151826541 100644
--- a/go/path_srv/internal/segutil/filter.go
+++ b/go/path_srv/internal/segutil/filter.go
@@ -22,6 +22,7 @@ import (
 	"github.com/scionproto/scion/go/lib/common"
 	"github.com/scionproto/scion/go/lib/ctrl/seg"
 	"github.com/scionproto/scion/go/lib/pathpol"
+	"github.com/scionproto/scion/go/lib/snet"
 )
 
 type Direction int
@@ -73,7 +74,7 @@ func psToSegs(ps pathpol.PathSet) seg.Segments {
 
 type segWrap struct {
 	intfs   []pathpol.PathInterface
-	key     string
+	key     snet.PathFingerprint
 	origSeg *seg.PathSegment
 }
 
@@ -103,13 +104,13 @@ func wrap(seg *seg.PathSegment, dir Direction) segWrap {
 	}
 	return segWrap{
 		intfs:   intfs,
-		key:     strings.Join(keyParts, " "),
+		key:     snet.PathFingerprint(strings.Join(keyParts, " ")),
 		origSeg: seg,
 	}
 }
 
 func (s segWrap) Interfaces() []pathpol.PathInterface { return s.intfs }
-func (s segWrap) Key() string                         { return s.key }
+func (s segWrap) Key() snet.PathFingerprint           { return s.key }
 
 type pathInterface struct {
 	ia   addr.IA
diff --git a/go/sciond/internal/fetcher/filter.go b/go/sciond/internal/fetcher/filter.go
index f14e8d864..09a174e0d 100644
--- a/go/sciond/internal/fetcher/filter.go
+++ b/go/sciond/internal/fetcher/filter.go
@@ -20,6 +20,7 @@ import (
 
 	"github.com/scionproto/scion/go/lib/infra/modules/combinator"
 	"github.com/scionproto/scion/go/lib/pathpol"
+	"github.com/scionproto/scion/go/lib/snet"
 )
 
 // Policy is a filter on path sets.
@@ -51,7 +52,7 @@ func psToPaths(ps pathpol.PathSet) []*combinator.Path {
 }
 
 type pathWrap struct {
-	key      string
+	key      snet.PathFingerprint
 	intfs    []pathpol.PathInterface
 	origPath *combinator.Path
 }
@@ -64,11 +65,11 @@ func newPathWrap(p *combinator.Path) pathWrap {
 		keyParts = append(keyParts, fmt.Sprintf("%s#%d", intf.IA(), intf.ID()))
 	}
 	return pathWrap{
-		key:      strings.Join(keyParts, " "),
+		key:      snet.PathFingerprint(strings.Join(keyParts, " ")),
 		intfs:    intfs,
 		origPath: p,
 	}
 }
 
 func (p pathWrap) Interfaces() []pathpol.PathInterface { return p.intfs }
-func (p pathWrap) Key() string                         { return p.key }
+func (p pathWrap) Key() snet.PathFingerprint           { return p.key }
diff --git a/go/sig/egress/iface/sesspath.go b/go/sig/egress/iface/sesspath.go
index 49e52e1c1..7e0a57746 100644
--- a/go/sig/egress/iface/sesspath.go
+++ b/go/sig/egress/iface/sesspath.go
@@ -24,15 +24,15 @@ import (
 
 // A SessPath contains a path and metadata related to path health.
 type SessPath struct {
-	key  string
+	key  snet.PathFingerprint
 	path snet.Path
 }
 
-func NewSessPath(key string, path snet.Path) *SessPath {
+func NewSessPath(key snet.PathFingerprint, path snet.Path) *SessPath {
 	return &SessPath{key: key, path: path}
 }
 
-func (sp *SessPath) Key() string {
+func (sp *SessPath) Key() snet.PathFingerprint {
 	return sp.key
 }
 
diff --git a/go/sig/egress/iface/sesspathpool.go b/go/sig/egress/iface/sesspathpool.go
index 811d11391..8d243274a 100644
--- a/go/sig/egress/iface/sesspathpool.go
+++ b/go/sig/egress/iface/sesspathpool.go
@@ -24,14 +24,14 @@ import (
 
 const pathFailExpiration = 5 * time.Minute
 
-type SessPathPool map[string]*SessPathStats
+type SessPathPool map[snet.PathFingerprint]*SessPathStats
 
 func NewSessPathPool() *SessPathPool {
 	return &SessPathPool{}
 }
 
 // Get returns the most suitable path. Excludes a specific path, if possible.
-func (spp SessPathPool) Get(exclude string) *SessPath {
+func (spp SessPathPool) Get(exclude snet.PathFingerprint) *SessPath {
 	var bestSessPath *SessPathStats
 	var minFail uint16 = math.MaxUint16
 	var bestNonExpiringSessPath *SessPathStats
@@ -66,7 +66,7 @@ func (spp SessPathPool) Get(exclude string) *SessPath {
 	return res.SessPath
 }
 
-func (spp SessPathPool) GetByKey(key string) *SessPath {
+func (spp SessPathPool) GetByKey(key snet.PathFingerprint) *SessPath {
 	res := spp[key]
 	if res == nil {
 		return nil
@@ -86,10 +86,10 @@ func (spp SessPathPool) Update(aps spathmeta.AppPathSet) {
 		}
 	}
 	for key, path := range aps {
-		e, ok := spp[string(key)]
+		e, ok := spp[key]
 		if !ok {
 			// This is a new path, add an entry.
-			spp[string(key)] = newSessPathStats(string(key), path)
+			spp[key] = newSessPathStats(key, path)
 		} else {
 			// This path already exists, update it.
 			e.SessPath.path = path
@@ -129,7 +129,7 @@ type SessPathStats struct {
 	failCount uint16
 }
 
-func newSessPathStats(key string, path snet.Path) *SessPathStats {
+func newSessPathStats(key snet.PathFingerprint, path snet.Path) *SessPathStats {
 	return &SessPathStats{
 		SessPath: NewSessPath(key, path),
 		lastFail: time.Now(),
diff --git a/go/sig/internal/pathmgr/pathmgr.go b/go/sig/internal/pathmgr/pathmgr.go
index 80ccaf036..900aa66ed 100644
--- a/go/sig/internal/pathmgr/pathmgr.go
+++ b/go/sig/internal/pathmgr/pathmgr.go
@@ -270,7 +270,7 @@ type pathWrap struct {
 	snet.Path
 }
 
-func (p pathWrap) Key() string { return p.Fingerprint() }
+func (p pathWrap) Key() snet.PathFingerprint { return p.Fingerprint() }
 func (p pathWrap) Interfaces() []pathpol.PathInterface {
 	intfs := make([]pathpol.PathInterface, 0, len(p.Path.Interfaces()))
 	for _, intf := range p.Path.Interfaces() {
diff --git a/go/sig/internal/snetmigrate/path.go b/go/sig/internal/snetmigrate/path.go
index 1368cd017..7a845351a 100644
--- a/go/sig/internal/snetmigrate/path.go
+++ b/go/sig/internal/snetmigrate/path.go
@@ -28,7 +28,7 @@ type emptyPath struct {
 	source addr.IA
 }
 
-func (p *emptyPath) Fingerprint() string {
+func (p *emptyPath) Fingerprint() snet.PathFingerprint {
 	return ""
 }
 


Test patch:
diff --git a/go/lib/infra/messenger/addr_test.go b/go/lib/infra/messenger/addr_test.go
index c54542a22..8564e9b04 100644
--- a/go/lib/infra/messenger/addr_test.go
+++ b/go/lib/infra/messenger/addr_test.go
@@ -203,7 +203,7 @@ func TestBuildFullAddress(t *testing.T) {
 		path := mock_snet.NewMockPath(ctrl)
 		path.EXPECT().Path().Return(&spath.Path{})
 		path.EXPECT().OverlayNextHop().Return(&net.UDPAddr{})
-		path.EXPECT().Fingerprint().Return("foo")
+		path.EXPECT().Fingerprint().Return(snet.PathFingerprint("foo"))
 		router.EXPECT().Route(gomock.Any(), gomock.Any()).Return(path, nil)
 		input := &snet.Addr{
 			IA:   remoteIA,
@@ -441,7 +441,7 @@ func initResolver(resolver *mock_messenger.MockResolver, f func(*mock_messenger.
 
 type testPath struct{}
 
-func (t *testPath) Fingerprint() string {
+func (t *testPath) Fingerprint() snet.PathFingerprint {
 	panic("not implemented")
 }
 
diff --git a/go/lib/pathpol/policy_test.go b/go/lib/pathpol/policy_test.go
index 68fc9f022..007e7acd5 100644
--- a/go/lib/pathpol/policy_test.go
+++ b/go/lib/pathpol/policy_test.go
@@ -27,6 +27,7 @@ import (
 
 	"github.com/scionproto/scion/go/lib/addr"
 	"github.com/scionproto/scion/go/lib/common"
+	"github.com/scionproto/scion/go/lib/snet"
 	"github.com/scionproto/scion/go/lib/xtest"
 	"github.com/scionproto/scion/go/lib/xtest/graph"
 )
@@ -636,21 +637,24 @@ func (p PathProvider) GetPaths(src, dst addr.IA) PathSet {
 			pathIntfs = append(pathIntfs, testPathIntf{ia: ia, ifid: ifid})
 			key.WriteString(fmt.Sprintf("%s-%d", ia, ifid))
 		}
-		result[key.String()] = &testPath{interfaces: pathIntfs, key: key.String()}
+		result[snet.PathFingerprint(key.String())] = &testPath{
+			interfaces: pathIntfs,
+			key:        snet.PathFingerprint(key.String()),
+		}
 	}
 	return result
 }
 
 type testPath struct {
 	interfaces []PathInterface
-	key        string
+	key        snet.PathFingerprint
 }
 
 func (p *testPath) Interfaces() []PathInterface {
 	return p.interfaces
 }
 
-func (p *testPath) Key() string { return p.key }
+func (p *testPath) Key() snet.PathFingerprint { return p.key }
 
 type testPathIntf struct {
 	ia   addr.IA
diff --git a/go/path_srv/internal/segutil/filter_test.go b/go/path_srv/internal/segutil/filter_test.go
index a6890415e..f7116917b 100644
--- a/go/path_srv/internal/segutil/filter_test.go
+++ b/go/path_srv/internal/segutil/filter_test.go
@@ -59,7 +59,7 @@ func TestFilter(t *testing.T) {
 					DoAndReturn(func(paths pathpol.PathSet,
 						f pathpol.FilterOptions) pathpol.PathSet {
 						for key := range paths {
-							if strings.Contains(key, "120") {
+							if strings.Contains(string(key), "120") {
 								delete(paths, key)
 							}
 						}
diff --git a/go/sciond/internal/fetcher/filter_test.go b/go/sciond/internal/fetcher/filter_test.go
index 49bf49f4e..aa36b2465 100644
--- a/go/sciond/internal/fetcher/filter_test.go
+++ b/go/sciond/internal/fetcher/filter_test.go
@@ -59,7 +59,7 @@ func TestFilter(t *testing.T) {
 				pol.EXPECT().Filter(gomock.Any()).DoAndReturn(
 					func(paths pathpol.PathSet) pathpol.PathSet {
 						for key := range paths {
-							if strings.Contains(key, "120") {
+							if strings.Contains(string(key), "120") {
 								delete(paths, key)
 							}
 						}
diff --git a/go/sig/internal/pathmgr/util_test.go b/go/sig/internal/pathmgr/util_test.go
index 8a738829b..2135c1c52 100644
--- a/go/sig/internal/pathmgr/util_test.go
+++ b/go/sig/internal/pathmgr/util_test.go
@@ -66,7 +66,7 @@ func mustIfID(t testing.TB, s string) common.IFIDType {
 	return common.IFIDType(ifID)
 }
 
-func fingerprint(interfaces []snet.PathInterface) string {
+func fingerprint(interfaces []snet.PathInterface) snet.PathFingerprint {
 	if len(interfaces) == 0 {
 		return ""
 	}
@@ -75,7 +75,7 @@ func fingerprint(interfaces []snet.PathInterface) string {
 		binary.Write(h, common.Order, intf.IA().IAInt())
 		binary.Write(h, common.Order, intf.ID())
 	}
-	return string(h.Sum(nil))
+	return snet.PathFingerprint(h.Sum(nil))
 }
 
 type intf struct {