Skip to content

Commit

Permalink
snet/sciond: add static info metadata fields to snet.PathMetadata (#3924
Browse files Browse the repository at this point in the history
)

Add static info metadata fields, from their temporary location in the path combinator to `snet.PathMetadata`.
Add these fields to the sciond protobuf protocol definitions to transport this information along with sciond path replies.
  • Loading branch information
matzf authored Nov 12, 2020
1 parent b3bbb1a commit 3c7fb10
Show file tree
Hide file tree
Showing 11 changed files with 673 additions and 301 deletions.
14 changes: 10 additions & 4 deletions go/lib/infra/modules/combinator/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,14 +385,20 @@ func (solution *pathSolution) Path() Path {

interfaces := segments.Interfaces()
asEntries := segments.ASEntries()
_ = collectMetadata(interfaces, asEntries) // TODO(matzf) export this
staticInfo := collectMetadata(interfaces, asEntries)

return Path{
SPath: segments.SPath(),
Metadata: snet.PathMetadata{
Interfaces: interfaces,
MTU: mtu,
Expiry: segments.ComputeExpTime(),
Interfaces: interfaces,
MTU: mtu,
Expiry: segments.ComputeExpTime(),
Latency: staticInfo.Latency,
Bandwidth: staticInfo.Bandwidth,
Geo: staticInfo.Geo,
LinkType: staticInfo.LinkType,
InternalHops: staticInfo.InternalHops,
Notes: staticInfo.Notes,
},
Weight: solution.cost,
}
Expand Down
85 changes: 22 additions & 63 deletions go/lib/infra/modules/combinator/staticinfo_accumulator.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,6 @@ import (
"github.com/scionproto/scion/go/lib/snet"
)

// XXX(matzf) these types are only here temporarily, to keep changes localized for the current PR.
// This will be moved to snet.PathMetadata, making this available to applications.

// TODO(matzf) document where does this info come from and how much to trust it.
type PathMetadata struct {
// Latency lists the latencies between any two consecutive interfaces.
// Entry i describes the latency between interface i and i+1.
// Consequently, there are N-1 entries for N interfaces.
// A 0-value indicates that the AS did not announce a latency for this hop.
Latency []time.Duration

// Bandwidth lists the bandwidth between any two consecutive interfaces, in Kbit/s.
// Entry i describes the bandwidth between interfaces i and i+1.
// A 0-value indicates that the AS did not announce a bandwidth for this hop.
Bandwidth []uint64

// Geo lists the geographical position of the border routers along the path.
// Entry i describes the position of the router for interface i.
// A 0-value indicates that the AS did not announce a position for this router.
Geo []GeoCoordinates

// LinkType contains the announced link type of inter-domain links.
// Entry i describes the link between interfaces 2*i and 2*i+1.
LinkType []LinkType

// InternalHops lists the number of AS internal hops for the ASes on path.
// Entry i describes the hop between interfaces 2*i+1 and 2*i+2 in the same AS.
// Consequently, there are no entries for the first and last ASes, as these
// are not traversed completely by the path.
InternalHops []uint32

// Notes contains the notes added by ASes on the path, in the order of occurrence.
// Entry i is the note of AS i on the path.
Notes []string
}

type LinkType uint8

const (
LinkTypeUnset LinkType = iota
LinkTypeDirect
LinkTypeMultihop
LinkTypeOpennet
)

type GeoCoordinates staticinfo.GeoCoordinates

// pathInfo is a helper to extract the StaticInfo metadata, using the information
// of the path already created from the pathSolution.
type pathInfo struct {
Expand All @@ -71,9 +24,11 @@ type pathInfo struct {
RemoteIF map[snet.PathInterface]snet.PathInterface
}

func collectMetadata(interfaces []snet.PathInterface, asEntries []seg.ASEntry) PathMetadata {
// collectMetadata extracts the StaticInfo metadata. The returned snet.PathMetadata
// contains Latency, Bandwidth, Geo, LinkType, InternalHops and Notes.
func collectMetadata(interfaces []snet.PathInterface, asEntries []seg.ASEntry) snet.PathMetadata {
if len(interfaces) == 0 {
return PathMetadata{}
return snet.PathMetadata{}
}
if len(interfaces)%2 != 0 {
panic("the number of interfaces traversed by the path is expected to be even")
Expand All @@ -89,7 +44,7 @@ func collectMetadata(interfaces []snet.PathInterface, asEntries []seg.ASEntry) P
}

path := pathInfo{interfaces, asEntries, remoteIF}
return PathMetadata{
return snet.PathMetadata{
Latency: collectLatency(path),
Bandwidth: collectBandwidth(path),
Geo: collectGeo(path),
Expand Down Expand Up @@ -207,30 +162,34 @@ func addHopBandwidth(m map[hopKey]uint64, a, b snet.PathInterface, v uint64) {
}
}

func collectGeo(p pathInfo) []GeoCoordinates {
ifaceGeos := make(map[snet.PathInterface]GeoCoordinates)
func collectGeo(p pathInfo) []snet.GeoCoordinates {
ifaceGeos := make(map[snet.PathInterface]snet.GeoCoordinates)
for _, asEntry := range p.ASEntries {
staticInfo := asEntry.Extensions.StaticInfo
if staticInfo == nil {
continue
}
for ifid, v := range staticInfo.Geo {
iface := snet.PathInterface{IA: asEntry.Local, ID: ifid}
ifaceGeos[iface] = GeoCoordinates(v)
ifaceGeos[iface] = snet.GeoCoordinates{
Longitude: v.Longitude,
Latitude: v.Latitude,
Address: v.Address,
}
}
}

geos := make([]GeoCoordinates, len(p.Interfaces))
geos := make([]snet.GeoCoordinates, len(p.Interfaces))
for i, iface := range p.Interfaces {
geos[i] = ifaceGeos[iface]
}
return geos
}

func collectLinkType(p pathInfo) []LinkType {
func collectLinkType(p pathInfo) []snet.LinkType {
// 1) Gather map with all the LinkTypes, identified by the (unordered)
// interface pair associated with each link
hopLinkTypes := make(map[hopKey]LinkType)
hopLinkTypes := make(map[hopKey]snet.LinkType)
for _, asEntry := range p.ASEntries {
staticInfo := asEntry.Extensions.StaticInfo
if staticInfo == nil {
Expand All @@ -243,7 +202,7 @@ func collectLinkType(p pathInfo) []LinkType {
if prevLinkType, duplicate := hopLinkTypes[hop]; duplicate {
// Handle conflicts by using LinkTypeUnset
if prevLinkType != linkType {
hopLinkTypes[hop] = LinkTypeUnset
hopLinkTypes[hop] = snet.LinkTypeUnset
}
} else {
hopLinkTypes[hop] = linkType
Expand All @@ -252,23 +211,23 @@ func collectLinkType(p pathInfo) []LinkType {
}

// 2) Go over the path; for each inter-AS link interface pair, add the link type
linkTypes := make([]LinkType, len(p.Interfaces)/2)
linkTypes := make([]snet.LinkType, len(p.Interfaces)/2)
for i := 0; i < len(p.Interfaces); i += 2 {
linkTypes[i/2] = hopLinkTypes[makeHopKey(p.Interfaces[i], p.Interfaces[i+1])]
}
return linkTypes
}

func convertLinkType(lt staticinfo.LinkType) LinkType {
func convertLinkType(lt staticinfo.LinkType) snet.LinkType {
switch lt {
case staticinfo.LinkTypeDirect:
return LinkTypeDirect
return snet.LinkTypeDirect
case staticinfo.LinkTypeMultihop:
return LinkTypeMultihop
return snet.LinkTypeMultihop
case staticinfo.LinkTypeOpennet:
return LinkTypeOpennet
return snet.LinkTypeOpennet
default:
return LinkTypeUnset
return snet.LinkTypeUnset
}
}

Expand Down
10 changes: 5 additions & 5 deletions go/lib/infra/modules/combinator/staticinfo_accumulator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,16 @@ func checkInternalHops(t *testing.T, g *graph.Graph,
assert.Equal(t, expected, internalHops)
}

func checkGeo(t *testing.T, g *graph.Graph, path []snet.PathInterface, geos []GeoCoordinates) {
func checkGeo(t *testing.T, g *graph.Graph, path []snet.PathInterface, geos []snet.GeoCoordinates) {
if len(path) == 0 {
assert.Empty(t, geos)
return
}

expected := []GeoCoordinates{}
expected := []snet.GeoCoordinates{}
for _, iface := range path {
e := g.GeoCoordinates(iface.ID)
expected = append(expected, GeoCoordinates{
expected = append(expected, snet.GeoCoordinates{
Longitude: e.Longitude,
Latitude: e.Latitude,
Address: e.Address,
Expand All @@ -191,14 +191,14 @@ func checkGeo(t *testing.T, g *graph.Graph, path []snet.PathInterface, geos []Ge
}

func checkLinkType(t *testing.T, g *graph.Graph,
path []snet.PathInterface, linkTypes []LinkType) {
path []snet.PathInterface, linkTypes []snet.LinkType) {

if len(path) == 0 {
assert.Empty(t, linkTypes)
return
}

expected := []LinkType{}
expected := []snet.LinkType{}
for i := 0; i < len(path); i += 2 {
expected = append(expected, convertLinkType(g.LinkType(path[i].ID, path[i+1].ID)))
}
Expand Down
50 changes: 43 additions & 7 deletions go/lib/sciond/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,21 +203,57 @@ func convertPath(p *sdpb.Path, dst addr.IA) (path.Path, error) {
if err != nil {
return path.Path{}, serrors.WrapStr("resolving underlay", err)
}
interfaces := make([]snet.PathInterface, 0, len(p.Interfaces))
for _, pi := range p.Interfaces {
interfaces = append(interfaces, snet.PathInterface{
interfaces := make([]snet.PathInterface, len(p.Interfaces))
for i, pi := range p.Interfaces {
interfaces[i] = snet.PathInterface{
ID: common.IFIDType(pi.Id),
IA: addr.IAInt(pi.IsdAs).IA(),
})
}
}
latency := make([]time.Duration, len(p.Latency))
for i, v := range p.Latency {
latency[i] = time.Second*time.Duration(v.Seconds) + time.Duration(v.Nanos)
}
geo := make([]snet.GeoCoordinates, len(p.Geo))
for i, v := range p.Geo {
geo[i] = snet.GeoCoordinates{
Latitude: v.Latitude,
Longitude: v.Longitude,
Address: v.Address,
}
}
linkType := make([]snet.LinkType, len(p.LinkType))
for i, v := range p.LinkType {
linkType[i] = linkTypeFromPB(v)
}

return path.Path{
Dst: dst,
SPath: spath.Path{Raw: p.Raw, Type: slayers.PathTypeSCION},
NextHop: underlayA,
Meta: snet.PathMetadata{
Interfaces: interfaces,
MTU: uint16(p.Mtu),
Expiry: expiry,
Interfaces: interfaces,
MTU: uint16(p.Mtu),
Expiry: expiry,
Latency: latency,
Bandwidth: p.Bandwidth,
Geo: geo,
LinkType: linkType,
InternalHops: p.InternalHops,
Notes: p.Notes,
},
}, nil
}

func linkTypeFromPB(lt sdpb.LinkType) snet.LinkType {
switch lt {
case sdpb.LinkType_LINK_TYPE_DIRECT:
return snet.LinkTypeDirect
case sdpb.LinkType_LINK_TYPE_MULTI_HOP:
return snet.LinkTypeMultihop
case sdpb.LinkType_LINK_TYPE_OPEN_NET:
return snet.LinkTypeOpennet
default:
return snet.LinkTypeUnset
}
}
74 changes: 71 additions & 3 deletions go/lib/snet/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,94 @@ func (iface PathInterface) String() string {
}

// PathMetadata contains supplementary information about a path.
//
// The information about MTU, Latency, Bandwidth etc. are based solely on data
// contained in the AS entries in the path construction beacons. These entries
// are signed/verified based on the control plane PKI. However, the
// *correctness* of this meta data has *not* been checked.
type PathMetadata struct {
// Interfaces is a list of interfaces on the path.
Interfaces []PathInterface

// MTU is the maximum transmission unit for the path, in bytes.
MTU uint16

// Expiry is the expiration time of the path.
Expiry time.Time

// Latency lists the latencies between any two consecutive interfaces.
// Entry i describes the latency between interface i and i+1.
// Consequently, there are N-1 entries for N interfaces.
// A 0-value indicates that the AS did not announce a latency for this hop.
Latency []time.Duration

// Bandwidth lists the bandwidth between any two consecutive interfaces, in Kbit/s.
// Entry i describes the bandwidth between interfaces i and i+1.
// A 0-value indicates that the AS did not announce a bandwidth for this hop.
Bandwidth []uint64

// Geo lists the geographical position of the border routers along the path.
// Entry i describes the position of the router for interface i.
// A 0-value indicates that the AS did not announce a position for this router.
Geo []GeoCoordinates

// LinkType contains the announced link type of inter-domain links.
// Entry i describes the link between interfaces 2*i and 2*i+1.
LinkType []LinkType

// InternalHops lists the number of AS internal hops for the ASes on path.
// Entry i describes the hop between interfaces 2*i+1 and 2*i+2 in the same AS.
// Consequently, there are no entries for the first and last ASes, as these
// are not traversed completely by the path.
InternalHops []uint32

// Notes contains the notes added by ASes on the path, in the order of occurrence.
// Entry i is the note of AS i on the path.
Notes []string
}

func (pm *PathMetadata) Copy() *PathMetadata {
if pm == nil {
return nil
}
return &PathMetadata{
Interfaces: append(pm.Interfaces[:0:0], pm.Interfaces...),
MTU: pm.MTU,
Expiry: pm.Expiry,
Interfaces: append(pm.Interfaces[:0:0], pm.Interfaces...),
MTU: pm.MTU,
Expiry: pm.Expiry,
Latency: append(pm.Latency[:0:0], pm.Latency...),
Bandwidth: append(pm.Bandwidth[:0:0], pm.Bandwidth...),
Geo: append(pm.Geo[:0:0], pm.Geo...),
LinkType: append(pm.LinkType[:0:0], pm.LinkType...),
InternalHops: append(pm.InternalHops[:0:0], pm.InternalHops...),
Notes: append(pm.Notes[:0:0], pm.Notes...),
}
}

// LinkType describes the underlying network for inter-domain links.
type LinkType uint8

// LinkType values
const (
// LinkTypeUnset represents an unspecified link type.
LinkTypeUnset LinkType = iota
// LinkTypeDirect represents a direct physical connection.
LinkTypeDirect
// LinkTypeMultihop represents a connection with local routing/switching.
LinkTypeMultihop
// LinkTypeOpennet represents a connection overlayed over publicly routed Internet.
LinkTypeOpennet
)

// GeoCoordinates describes a geographical position (of a border router on the path).
type GeoCoordinates struct {
// Latitude of the geographic coordinate, in the WGS 84 datum.
Latitude float32
// Longitude of the geographic coordinate, in the WGS 84 datum.
Longitude float32
// Civic address of the location.
Address string
}

type PathFingerprint string

func (pf PathFingerprint) String() string {
Expand Down
Loading

0 comments on commit 3c7fb10

Please sign in to comment.