Repository: scionproto/scion
Pull request #3811 (2020-06-30 13:45:44)

cs: enable v2 beaconing through feature flag
Create v2 path segment extender if feature flag is set.
Construct v2 paths for registering down paths if feature flag is set.

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


Oncilla: /rebase


Patch:
diff --git a/go/cs/beaconing/BUILD.bazel b/go/cs/beaconing/BUILD.bazel
index 67a363561..f5c2e8241 100644
--- a/go/cs/beaconing/BUILD.bazel
+++ b/go/cs/beaconing/BUILD.bazel
@@ -33,7 +33,6 @@ go_library(
         "//go/lib/serrors:go_default_library",
         "//go/lib/slayers/path: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",
@@ -71,6 +70,7 @@ go_test(
         "//go/lib/scrypto/cppki:go_default_library",
         "//go/lib/serrors: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 71da04799..9124f4480 100644
--- a/go/cs/beaconing/registrar.go
+++ b/go/cs/beaconing/registrar.go
@@ -30,7 +30,6 @@ 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"
 )
@@ -51,6 +50,11 @@ 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
@@ -61,13 +65,12 @@ type Registrar struct {
 	Provider SegmentProvider
 	Store    SegmentStore
 	RPC      RPC
+	Pather   Pather
 	IA       addr.IA
 	Signer   ctrl.Signer
 	Intfs    *ifstate.Interfaces
 	Type     proto.PathSegType
 
-	TopoProvider topology.Provider // FIXME(roosd): reduce this to the minimal required interface.
-
 	// tick is mutable.
 	Tick     Tick
 	lastSucc time.Time
@@ -134,11 +137,11 @@ func (r *Registrar) registerRemote(ctx context.Context, segments <-chan beacon.B
 		}
 		expected++
 		s := remoteRegistrar{
-			segType:      r.Type,
-			rpc:          r.RPC,
-			topoProvider: r.TopoProvider,
-			summary:      s,
-			wg:           &wg,
+			segType: r.Type,
+			rpc:     r.RPC,
+			pather:  r.Pather,
+			summary: s,
+			wg:      &wg,
 		}
 
 		// Avoid head-of-line blocking when sending message to slow servers.
@@ -210,18 +213,18 @@ func (r *Registrar) logSummary(logger log.Logger, s *summary) {
 
 // remoteRegistrar registers one segment with the path server.
 type remoteRegistrar struct {
-	segType      proto.PathSegType
-	rpc          RPC
-	topoProvider topology.Provider
-	summary      *summary
-	wg           *sync.WaitGroup
+	segType proto.PathSegType
+	rpc     RPC
+	pather  Pather
+	summary *summary
+	wg      *sync.WaitGroup
 }
 
 // start extends the beacon and starts a go routine that registers the beacon
 // with the path server.
 func (r *remoteRegistrar) start(ctx context.Context, bseg beacon.Beacon) {
 	logger := log.FromCtx(ctx)
-	addr, err := addrutil.GetPath(addr.SvcPS, bseg.Segment, r.topoProvider)
+	addr, err := r.pather.GetPath(addr.SvcPS, bseg.Segment)
 	if err != nil {
 		metrics.Registrar.InternalErrorsWithType(r.segType.String()).Inc()
 		logger.Error("[beaconing.Registrar] Unable to choose server", "err", err)
diff --git a/go/lib/snet/addrutil/BUILD.bazel b/go/lib/snet/addrutil/BUILD.bazel
index a0e60f87f..0327e2452 100644
--- a/go/lib/snet/addrutil/BUILD.bazel
+++ b/go/lib/snet/addrutil/BUILD.bazel
@@ -10,6 +10,8 @@ go_library(
         "//go/lib/common:go_default_library",
         "//go/lib/ctrl/seg:go_default_library",
         "//go/lib/serrors:go_default_library",
+        "//go/lib/slayers/path:go_default_library",
+        "//go/lib/slayers/path/scion:go_default_library",
         "//go/lib/snet:go_default_library",
         "//go/lib/spath:go_default_library",
         "//go/lib/topology:go_default_library",
diff --git a/go/lib/snet/addrutil/addrutil.go b/go/lib/snet/addrutil/addrutil.go
index ff01cb287..e197fed68 100644
--- a/go/lib/snet/addrutil/addrutil.go
+++ b/go/lib/snet/addrutil/addrutil.go
@@ -16,17 +16,91 @@ package addrutil
 
 import (
 	"bytes"
+	"encoding/binary"
 	"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/serrors"
+	"github.com/scionproto/scion/go/lib/slayers/path"
+	"github.com/scionproto/scion/go/lib/slayers/path/scion"
 	"github.com/scionproto/scion/go/lib/snet"
 	"github.com/scionproto/scion/go/lib/spath"
 	"github.com/scionproto/scion/go/lib/topology"
 )
 
+type Pather 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) {
+	if len(ps.ASEntries) == 0 {
+		return nil, serrors.New("empty path")
+	}
+
+	beta := ps.SData.SegID
+	// The hop fields need to be in reversed order.
+	hopFields := make([]*path.HopField, len(ps.ASEntries))
+	for i, entry := range ps.ASEntries {
+		if len(entry.HopEntries) == 0 {
+			return nil, serrors.New("hop with no entry", "index", i)
+		}
+		hopFields[len(hopFields)-1-i] = &path.HopField{
+			ConsIngress: entry.HopEntries[0].HopField.ConsIngress,
+			ConsEgress:  entry.HopEntries[0].HopField.ConsEgress,
+			ExpTime:     entry.HopEntries[0].HopField.ExpTime,
+			Mac:         entry.HopEntries[0].HopField.MAC,
+		}
+		beta = beta ^ binary.BigEndian.Uint16(entry.HopEntries[0].HopField.MAC[:2])
+	}
+
+	hops := len(hopFields) - 1
+	dec := scion.Decoded{
+		Base: scion.Base{
+			PathMeta: scion.MetaHdr{
+				CurrHF:  0,
+				CurrINF: 0,
+				SegLen:  [3]uint8{uint8(hops), 0, 0},
+			},
+			NumHops: hops,
+			NumINF:  1,
+		},
+		InfoFields: []*path.InfoField{{
+			Timestamp: ps.SData.RawTimestamp,
+			ConsDir:   false,
+			SegID:     beta,
+		}},
+		HopFields: hopFields,
+	}
+	raw := make([]byte, dec.Len())
+	if err := dec.SerializeTo(raw); err != nil {
+		return nil, serrors.WrapStr("serializing path", err)
+	}
+	ifID := dec.HopFields[0].ConsIngress
+	nextHop, ok := p.UnderlayNextHop(ifID)
+	if !ok {
+		return nil, serrors.New("first-hop border router not found", "intf_id", ifID)
+	}
+	return &snet.SVCAddr{
+		IA:      ps.FirstIA(),
+		Path:    spath.NewV2(raw),
+		NextHop: nextHop,
+		SVC:     svc,
+	}, nil
+
+}
+
+type LegacyPather struct {
+	TopoProvider topology.Provider
+}
+
+func (p LegacyPather) GetPath(svc addr.HostSVC, ps *seg.PathSegment) (net.Addr, 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) {
 	p, err := legacyPath(ps)
diff --git a/go/pkg/cs/BUILD.bazel b/go/pkg/cs/BUILD.bazel
index 97f1f824a..9e1042cd4 100644
--- a/go/pkg/cs/BUILD.bazel
+++ b/go/pkg/cs/BUILD.bazel
@@ -43,6 +43,7 @@ go_library(
         "//go/lib/scrypto:go_default_library",
         "//go/lib/serrors:go_default_library",
         "//go/lib/snet:go_default_library",
+        "//go/lib/snet/addrutil:go_default_library",
         "//go/lib/sock/reliable:go_default_library",
         "//go/lib/sock/reliable/reconnect:go_default_library",
         "//go/lib/spath:go_default_library",
diff --git a/go/pkg/cs/main.go b/go/pkg/cs/main.go
index b2492fe70..488e7905a 100644
--- a/go/pkg/cs/main.go
+++ b/go/pkg/cs/main.go
@@ -71,6 +71,7 @@ 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/spath"
@@ -476,6 +477,7 @@ func (app *App) Run() int {
 		return 1
 	}
 	Tasks = &PeriodicTasks{
+		headerV2:     Cfg.Features.HeaderV2,
 		args:         args,
 		intfs:        Intfs,
 		conn:         conn.(*snet.SCIONPacketConn),
@@ -546,6 +548,7 @@ type PeriodicTasks struct {
 	TopoProvider    topology.Provider
 	allowIsdLoop    bool
 	addressRewriter *messenger.AddressRewriter
+	headerV2        bool
 
 	Keepalive  *periodic.Runner
 	originator *periodic.Runner
@@ -689,16 +692,7 @@ func (t *PeriodicTasks) startOriginator(a *net.UDPAddr) (*periodic.Runner, error
 		return nil, nil
 	}
 	s := &beaconing.Originator{
-		Extender: &beaconing.LegacyExtender{
-			IA:         topo.IA(),
-			Signer:     t.signer,
-			MAC:        t.genMac,
-			Intfs:      t.intfs,
-			MTU:        topo.MTU(),
-			MaxExpTime: maxExpTimeFactory(t.store, beacon.PropPolicy),
-			StaticInfo: func() *beaconing.StaticInfoCfg { return staticInfoCfg },
-			Task:       "originator",
-		},
+		Extender: t.extender("propagator", topo, maxExpTimeFactory(t.store, beacon.PropPolicy)),
 		BeaconSender: &onehop.BeaconSender{
 			Sender: onehop.Sender{
 				Conn: t.conn,
@@ -720,16 +714,7 @@ func (t *PeriodicTasks) startOriginator(a *net.UDPAddr) (*periodic.Runner, error
 func (t *PeriodicTasks) startPropagator(a *net.UDPAddr) (*periodic.Runner, error) {
 	topo := t.TopoProvider.Get()
 	p := &beaconing.Propagator{
-		Extender: &beaconing.LegacyExtender{
-			IA:         topo.IA(),
-			Signer:     t.signer,
-			MAC:        t.genMac,
-			Intfs:      t.intfs,
-			MTU:        topo.MTU(),
-			MaxExpTime: maxExpTimeFactory(t.store, beacon.PropPolicy),
-			StaticInfo: func() *beaconing.StaticInfoCfg { return staticInfoCfg },
-			Task:       "propagator",
-		},
+		Extender: t.extender("propagator", topo, maxExpTimeFactory(t.store, beacon.PropPolicy)),
 		BeaconSender: &onehop.BeaconSender{
 			Sender: onehop.Sender{
 				Conn: t.conn,
@@ -776,28 +761,56 @@ func (t *PeriodicTasks) startSegRegRunners() (segRegRunners, error) {
 func (t *PeriodicTasks) startRegistrar(topo topology.Topology, segType proto.PathSegType,
 	policyType beacon.PolicyType) (*periodic.Runner, error) {
 
+	var pather beaconing.Pather = addrutil.LegacyPather{TopoProvider: t.TopoProvider}
+	if t.headerV2 {
+		pather = addrutil.Pather{
+			UnderlayNextHop: func(ifID uint16) (*net.UDPAddr, bool) {
+				return t.TopoProvider.Get().UnderlayNextHop2(common.IFIDType(ifID))
+			},
+		}
+	}
+
 	r := &beaconing.Registrar{
-		Extender: &beaconing.LegacyExtender{
+		Extender: t.extender("registrar", topo, maxExpTimeFactory(t.store, policyType)),
+		Provider: t.store,
+		Store:    &seghandler.DefaultStorage{PathDB: t.pathDB},
+		RPC:      beaconingcompat.RPC{Messenger: t.msgr},
+		IA:       topo.IA(),
+		Signer:   t.signer,
+		Intfs:    t.intfs,
+		Type:     segType,
+		Pather:   pather,
+		Tick:     beaconing.NewTick(Cfg.BS.RegistrationInterval.Duration),
+	}
+	return periodic.Start(r, 500*time.Millisecond, Cfg.BS.RegistrationInterval.Duration), nil
+}
+
+func (t *PeriodicTasks) extender(task string, topo topology.Topology,
+	maxExp func() spath.ExpTimeType) beaconing.Extender {
+
+	if !t.headerV2 {
+		return &beaconing.LegacyExtender{
 			IA:         topo.IA(),
 			Signer:     t.signer,
 			MAC:        t.genMac,
 			Intfs:      t.intfs,
 			MTU:        topo.MTU(),
-			MaxExpTime: maxExpTimeFactory(t.store, policyType),
+			MaxExpTime: maxExp,
 			StaticInfo: func() *beaconing.StaticInfoCfg { return staticInfoCfg },
-			Task:       "registrar",
-		},
-		Provider:     t.store,
-		Store:        &seghandler.DefaultStorage{PathDB: t.pathDB},
-		RPC:          beaconingcompat.RPC{Messenger: t.msgr},
-		IA:           topo.IA(),
-		Signer:       t.signer,
-		Intfs:        t.intfs,
-		Type:         segType,
-		TopoProvider: t.TopoProvider,
-		Tick:         beaconing.NewTick(Cfg.BS.RegistrationInterval.Duration),
+			Task:       task,
+		}
 	}
-	return periodic.Start(r, 500*time.Millisecond, Cfg.BS.RegistrationInterval.Duration), nil
+	return &beaconing.DefaultExtender{
+		IA:         topo.IA(),
+		Signer:     t.signer,
+		MAC:        t.genMac,
+		Intfs:      t.intfs,
+		MTU:        topo.MTU(),
+		MaxExpTime: func() uint8 { return uint8(maxExp()) },
+		StaticInfo: func() *beaconing.StaticInfoCfg { return staticInfoCfg },
+		Task:       task,
+	}
+
 }
 
 func (t *PeriodicTasks) Kill() {


Test patch:
diff --git a/go/cs/beaconing/registrar_test.go b/go/cs/beaconing/registrar_test.go
index afa29244c..bbe4305ee 100644
--- a/go/cs/beaconing/registrar_test.go
+++ b/go/cs/beaconing/registrar_test.go
@@ -44,6 +44,7 @@ import (
 	"github.com/scionproto/scion/go/lib/scrypto"
 	"github.com/scionproto/scion/go/lib/scrypto/cppki"
 	"github.com/scionproto/scion/go/lib/snet"
+	"github.com/scionproto/scion/go/lib/snet/addrutil"
 	"github.com/scionproto/scion/go/lib/xtest/graph"
 	"github.com/scionproto/scion/go/pkg/trust"
 	"github.com/scionproto/scion/go/proto"
@@ -102,14 +103,14 @@ func TestRegistrarRun(t *testing.T) {
 					MaxExpTime: maxExpTimeFactory(beacon.DefaultMaxExpTime),
 					StaticInfo: func() *StaticInfoCfg { return nil },
 				},
-				IA:           topoProvider.Get().IA(),
-				Signer:       testSigner(t, priv, topoProvider.Get().IA()),
-				Intfs:        intfs,
-				Tick:         NewTick(time.Hour),
-				Provider:     segProvider,
-				Store:        segStore,
-				TopoProvider: topoProvider,
-				Type:         test.segType,
+				IA:       topoProvider.Get().IA(),
+				Signer:   testSigner(t, priv, topoProvider.Get().IA()),
+				Intfs:    intfs,
+				Tick:     NewTick(time.Hour),
+				Provider: segProvider,
+				Store:    segStore,
+				Pather:   addrutil.LegacyPather{TopoProvider: topoProvider},
+				Type:     test.segType,
 			}
 			g := graph.NewDefaultGraph(mctrl)
 			segProvider.EXPECT().SegmentsToRegister(gomock.Any(), test.segType).DoAndReturn(
@@ -188,14 +189,14 @@ func TestRegistrarRun(t *testing.T) {
 					MaxExpTime: maxExpTimeFactory(beacon.DefaultMaxExpTime),
 					StaticInfo: func() *StaticInfoCfg { return nil },
 				},
-				IA:           topoProvider.Get().IA(),
-				Signer:       testSigner(t, priv, topoProvider.Get().IA()),
-				Intfs:        intfs,
-				Tick:         NewTick(time.Hour),
-				Provider:     segProvider,
-				TopoProvider: topoProvider,
-				Type:         test.segType,
-				RPC:          rpc,
+				IA:       topoProvider.Get().IA(),
+				Signer:   testSigner(t, priv, topoProvider.Get().IA()),
+				Intfs:    intfs,
+				Tick:     NewTick(time.Hour),
+				Provider: segProvider,
+				Pather:   addrutil.LegacyPather{TopoProvider: topoProvider},
+				Type:     test.segType,
+				RPC:      rpc,
 			}
 			g := graph.NewDefaultGraph(mctrl)
 			segProvider.EXPECT().SegmentsToRegister(gomock.Any(), test.segType).DoAndReturn(
@@ -279,13 +280,13 @@ func TestRegistrarRun(t *testing.T) {
 				MaxExpTime: maxExpTimeFactory(beacon.DefaultMaxExpTime),
 				StaticInfo: func() *StaticInfoCfg { return nil },
 			},
-			IA:           topoProvider.Get().IA(),
-			Signer:       testSigner(t, priv, topoProvider.Get().IA()),
-			Intfs:        intfs,
-			Tick:         NewTick(time.Hour),
-			Provider:     segProvider,
-			TopoProvider: topoProvider,
-			Type:         proto.PathSegType_core,
+			IA:       topoProvider.Get().IA(),
+			Signer:   testSigner(t, priv, topoProvider.Get().IA()),
+			Intfs:    intfs,
+			Tick:     NewTick(time.Hour),
+			Provider: segProvider,
+			Pather:   addrutil.LegacyPather{TopoProvider: topoProvider},
+			Type:     proto.PathSegType_core,
 		}
 		res := make(chan beacon.BeaconOrErr, 3)
 		segProvider.EXPECT().SegmentsToRegister(gomock.Any(), proto.PathSegType_core).DoAndReturn(
@@ -322,14 +323,14 @@ func TestRegistrarRun(t *testing.T) {
 				MaxExpTime: maxExpTimeFactory(beacon.DefaultMaxExpTime),
 				StaticInfo: func() *StaticInfoCfg { return nil },
 			},
-			IA:           topoProvider.Get().IA(),
-			Signer:       testSigner(t, priv, topoProvider.Get().IA()),
-			Intfs:        intfs,
-			Tick:         NewTick(time.Hour),
-			Provider:     segProvider,
-			TopoProvider: topoProvider,
-			Type:         proto.PathSegType_down,
-			RPC:          rpc,
+			IA:       topoProvider.Get().IA(),
+			Signer:   testSigner(t, priv, topoProvider.Get().IA()),
+			Intfs:    intfs,
+			Tick:     NewTick(time.Hour),
+			Provider: segProvider,
+			Pather:   addrutil.LegacyPather{TopoProvider: topoProvider},
+			Type:     proto.PathSegType_down,
+			RPC:      rpc,
 		}
 		g := graph.NewDefaultGraph(mctrl)
 		require.NoError(t, err)