Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add path selector context and explicit path query #260

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion bwtester/bwtestclient/bwtestclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ func runBwtest(local netip.AddrPort, serverCCAddr pan.UDPAddr, policy pan.Policy

// Pin DC to path used for request
if serverDCAddr.IA != clientDCAddr.IA {
dcConn.SetPolicy(pan.Pinned{ccSelector.Path().Fingerprint})
dcConn.SetPolicy(pan.Pinned{ccSelector.Path(context.Background()).Fingerprint})
}

// Start blasting client->server
Expand Down
3 changes: 2 additions & 1 deletion bwtester/bwtestserver/bwtestserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func runServer(listen netip.AddrPort) error {

results := make(resultsMap)

ctx := context.Background()
ccSelector := pan.NewDefaultReplySelector()
ccConn, err := pan.ListenUDP(context.Background(), listen, ccSelector)
if err != nil {
Expand Down Expand Up @@ -101,7 +102,7 @@ func runServer(listen netip.AddrPort) error {
if err != nil {
continue
}
path := ccSelector.Path(clientCCAddr.(pan.UDPAddr))
path := ccSelector.Path(ctx, clientCCAddr.(pan.UDPAddr))
finishTime, err := startBwtestBackground(serverCCAddr, clientCCAddr.(pan.UDPAddr), path,
clientBwp, serverBwp, currentResult)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions pkg/pan/pan.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,9 @@ type HostNotFoundError struct {
func (e HostNotFoundError) Error() string {
return fmt.Sprintf("host not found: '%s'", e.Host)
}

// Query paths to a particular destination AS.
func QueryPaths(ctx context.Context, dst IA) ([]*Path, error) {
paths, _, err := (&pool).paths(ctx, dst)
return paths, err
}
16 changes: 16 additions & 0 deletions pkg/pan/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,22 @@ func (p *Path) String() string {
}
}

// DataplaneLen returns the length of the path in the data plane.
func (p *Path) DataplaneLen() (int, error) {
switch dataplanePath := p.ForwardingPath.dataplanePath.(type) {
case snet.RawReplyPath:
return dataplanePath.Path.Len(), nil
case snet.RawPath:
return len(dataplanePath.Raw), nil
case snetpath.SCION:
return len(dataplanePath.Raw), nil
case snetpath.Empty:
return 0, nil
default:
return 0, fmt.Errorf("unsupported path type %T", p.ForwardingPath.dataplanePath)
}
}

// ForwardingPath represents a data plane forwarding path.
type ForwardingPath struct {
dataplanePath snet.DataplanePath
Expand Down
77 changes: 77 additions & 0 deletions pkg/pan/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import (
"time"

"github.com/scionproto/scion/pkg/slayers/path/scion"
"github.com/scionproto/scion/pkg/snet"
snetpath "github.com/scionproto/scion/pkg/snet/path"
"github.com/stretchr/testify/assert"
)

Expand Down Expand Up @@ -75,6 +77,81 @@ func TestPathString(t *testing.T) {
}
}

func TestDataplaneLen(t *testing.T) {
cases := []struct {
name string
path *Path
length int
expectErr bool
}{
{
name: "raw reply path",
path: &Path{ForwardingPath: ForwardingPath{
dataplanePath: snet.RawReplyPath{
Path: &scion.Raw{
Base: scion.Base{
PathMeta: scion.MetaHdr{
CurrINF: 1, CurrHF: 3, SegLen: [3]uint8{2, 2, 0},
},
NumINF: 2,
NumHops: 4,
},
Raw: make([]byte, 68),
},
},
}},
length: 68,
expectErr: false,
},
{
name: "raw path",
path: &Path{ForwardingPath: ForwardingPath{
dataplanePath: snet.RawPath{
PathType: scion.PathType,
Raw: make([]byte, 68),
},
}},
length: 68,
expectErr: false,
},
{
name: "scion path",
path: &Path{ForwardingPath: ForwardingPath{
dataplanePath: snetpath.SCION{
Raw: make([]byte, 68),
},
}},
length: 68,
expectErr: false,
},
{
name: "empty path",
path: &Path{ForwardingPath: ForwardingPath{
dataplanePath: snetpath.Empty{},
}},
length: 0,
expectErr: false,
},
{
name: "unsupported (one hop) path",
path: &Path{ForwardingPath: ForwardingPath{
dataplanePath: snetpath.OneHop{},
}},
length: 0,
expectErr: true,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
l, err := c.path.DataplaneLen()
assert.Equal(t, c.expectErr, err != nil)
if err == nil {
assert.Equal(t, c.length, l)
}
})
}
}

func TestInterfacesFromDecoded(t *testing.T) {
// Not a great test case...
rawPath := []byte("\x00\x00\x20\x80\x00\x00\x01\x11\x00\x00\x01\x00\x01\x00\x02\x22\x00\x00" +
Expand Down
6 changes: 5 additions & 1 deletion pkg/pan/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ func (p *pathPool) paths(ctx context.Context, dstIA IA) ([]*Path, bool, error) {

// queryPaths returns paths to dstIA. Unconditionally requests paths from sciond.
func (p *pathPool) queryPaths(ctx context.Context, dstIA IA) ([]*Path, error) {
paths, err := host().queryPaths(ctx, dstIA)
host, err := getHost()
if err != nil {
return nil, err
}
paths, err := host.queryPaths(ctx, dstIA)
if err != nil {
return nil, err
}
Expand Down
34 changes: 26 additions & 8 deletions pkg/pan/sciond.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,36 @@ const (
initTimeout = 1 * time.Second
)

// HostContextError is returned by the API if the SCION host context initialization
// failed. This error most likely appears if the SCION daemon is not running or
// can't be reached.
type HostContextError struct {
Cause error
}

func (e HostContextError) Error() string {
return fmt.Sprintf("error initializing SCION host context: '%v'", e.Cause)
}

var singletonHostContext hostContext
var initOnce sync.Once
var initOnce = sync.OnceValue(mustInitHostContext)

// host initialises and returns the singleton hostContext.
func host() *hostContext {
initOnce.Do(mustInitHostContext)
return &singletonHostContext
func getHost() (*hostContext, error) {
err := initOnce()
if err != nil {
return nil, err
}
return &singletonHostContext, nil
}

func mustInitHostContext() {
func mustInitHostContext() error {
hostCtx, err := initHostContext()
if err != nil {
fmt.Fprintf(os.Stderr, "Error initializing SCION host context: %v\n", err)
os.Exit(1)
return HostContextError{Cause: err}
}
singletonHostContext = hostCtx
return nil
}

func initHostContext() (hostContext, error) {
Expand Down Expand Up @@ -108,7 +122,11 @@ func findAnyHostInLocalAS(ctx context.Context, sciondConn daemon.Connector) (net
// wildcard addresses in snet.
// See note on wildcard addresses in the package documentation.
func defaultLocalIP() (netip.Addr, error) {
stdIP, err := addrutil.ResolveLocal(host().hostInLocalAS)
host, err := getHost()
if err != nil {
return netip.Addr{}, err
}
stdIP, err := addrutil.ResolveLocal(host.hostInLocalAS)
ip, ok := netip.AddrFromSlice(stdIP)
if err != nil || !ok {
return netip.Addr{}, fmt.Errorf("unable to resolve default local address %w", err)
Expand Down
12 changes: 8 additions & 4 deletions pkg/pan/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import (
type Selector interface {
// Path selects the path for the next packet.
// Invoked for each packet sent with Write.
Path() *Path
Path(ctx context.Context) *Path
// Initialize the selector for a connection with the initial list of paths,
// filtered/ordered by the Policy.
// Invoked once during the creation of a Conn.
Expand Down Expand Up @@ -64,7 +64,7 @@ func NewDefaultSelector() *DefaultSelector {
return &DefaultSelector{}
}

func (s *DefaultSelector) Path() *Path {
func (s *DefaultSelector) Path(_ context.Context) *Path {
s.mutex.Lock()
defer s.mutex.Unlock()

Expand Down Expand Up @@ -143,7 +143,7 @@ func (s *PingingSelector) SetActive(numActive int) {
atomic.SwapInt64(&s.numActive, int64(numActive))
}

func (s *PingingSelector) Path() *Path {
func (s *PingingSelector) Path(_ context.Context) *Path {
s.mutex.Lock()
defer s.mutex.Unlock()

Expand Down Expand Up @@ -186,6 +186,10 @@ func (s *PingingSelector) ensureRunning() {
s.mutex.Lock()
defer s.mutex.Unlock()

host, err := getHost()
if err != nil {
return
}
if s.local.IA == s.remote.IA {
return
}
Expand All @@ -194,7 +198,7 @@ func (s *PingingSelector) ensureRunning() {
}
s.pingerCtx, s.pingerCancel = context.WithCancel(context.Background())
local := s.local.snetUDPAddr()
pinger, err := ping.NewPinger(s.pingerCtx, host().sciond, local)
pinger, err := ping.NewPinger(s.pingerCtx, host.sciond, local)
if err != nil {
return
}
Expand Down
29 changes: 24 additions & 5 deletions pkg/pan/udp_dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ type Conn interface {
// SetPolicy allows to set the path policy for paths used by Write, at any
// time.
SetPolicy(policy Policy)
// WriteWithCtx writes a message to the remote address using a path from the
// path policy and selector. ctx is passed to the path selector where it can
// provide additional user-defined information, e.g., whether the packet is
// urgent or not.
WriteWithCtx(ctx context.Context, b []byte) (n int, err error)
// WriteVia writes a message to the remote address via the given path.
// This bypasses the path policy and selector used for Write.
WriteVia(path *Path, b []byte) (int, error)
Expand All @@ -36,6 +41,7 @@ type Conn interface {
ReadVia(b []byte) (int, *Path, error)

GetPath() *Path
GetPathWithCtx(ctx context.Context) *Path
}

// DialUDP opens a SCION/UDP socket, connected to the remote address.
Expand All @@ -50,13 +56,18 @@ type Conn interface {
func DialUDP(ctx context.Context, local netip.AddrPort, remote UDPAddr,
policy Policy, selector Selector) (Conn, error) {

local, err := defaultLocalAddr(local)
host, err := getHost()
if err != nil {
return nil, err
}

local, err = defaultLocalAddr(local)
if err != nil {
return nil, err
}

sn := snet.SCIONNetwork{
Topology: host().sciond,
Topology: host.sciond,
SCMPHandler: scmpHandler{},
}
conn, err := sn.OpenRaw(ctx, net.UDPAddrFromAddrPort(local))
Expand All @@ -65,7 +76,7 @@ func DialUDP(ctx context.Context, local netip.AddrPort, remote UDPAddr,
}
ipport := conn.LocalAddr().(*net.UDPAddr).AddrPort()
localUDPAddr := UDPAddr{
IA: host().ia,
IA: host.ia,
IP: ipport.Addr(),
Port: ipport.Port(),
}
Expand Down Expand Up @@ -110,20 +121,28 @@ func (c *dialedConn) LocalAddr() net.Addr {
}

func (c *dialedConn) GetPath() *Path {
return c.GetPathWithCtx(context.TODO())
}

func (c *dialedConn) GetPathWithCtx(ctx context.Context) *Path {
if c.selector == nil {
return nil
}
return c.selector.Path()
return c.selector.Path(ctx)
}

func (c *dialedConn) RemoteAddr() net.Addr {
return c.remote
}

func (c *dialedConn) Write(b []byte) (int, error) {
return c.WriteWithCtx(context.TODO(), b)
}

func (c *dialedConn) WriteWithCtx(ctx context.Context, b []byte) (int, error) {
var path *Path
if c.local.IA != c.remote.IA {
path = c.selector.Path()
path = c.selector.Path(ctx)
if path == nil {
return 0, errNoPathTo(c.remote.IA)
}
Expand Down
Loading