From f02f0d94ef6f5f7843230bc76f575b2e25f3554b Mon Sep 17 00:00:00 2001 From: Lukas Vogel Date: Mon, 9 Sep 2019 09:34:34 +0200 Subject: [PATCH 1/5] showpaths: Fix probing Also abstract the probing into its own library so that other modules can also use it. Also introduce an acceptance test for showpaths probing. Fixes #3126 --- acceptance/showpaths_status_acceptance/test | 76 +++++++ acceptance/sig_short_exp_time_acceptance/test | 13 ++ go/lib/sciond/pathprobe/BUILD.bazel | 19 ++ go/lib/sciond/pathprobe/paths.go | 196 ++++++++++++++++++ go/protobuf/BUILD.bazel | 16 ++ go/tools/showpaths/BUILD.bazel | 5 +- go/tools/showpaths/paths.go | 111 ++-------- 7 files changed, 333 insertions(+), 103 deletions(-) create mode 100755 acceptance/showpaths_status_acceptance/test create mode 100644 go/lib/sciond/pathprobe/BUILD.bazel create mode 100644 go/lib/sciond/pathprobe/paths.go create mode 100644 go/protobuf/BUILD.bazel diff --git a/acceptance/showpaths_status_acceptance/test b/acceptance/showpaths_status_acceptance/test new file mode 100755 index 0000000000..7c937ac4e1 --- /dev/null +++ b/acceptance/showpaths_status_acceptance/test @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +# 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. + + +import logging + +from plumbum import local + +from acceptance.common.log import LogExec, init_log +from acceptance.common.base import CmdBase, TestBase, set_name + + +set_name(__file__) +logger = logging.getLogger(__name__) + + +class Test(TestBase): + """ + Test that showpaths with the -p option works as intended. + """ + +class Base(CmdBase): + pass + +@Test.subcommand('setup') +class TestSetup(Base): + + @LogExec(logger, 'setup') + def main(self): + self.scion.topology('topology/Tiny.topo') + self.scion.run() + if not self.no_docker: + self.tools_dc('start', 'tester*') + self.docker_status() + +@Test.subcommand("run") +class TestRun(Base): + showpaths = local['./bin/showpaths'] + + @LogExec(logger, "run") + def main(self): + if self.no_docker: + out = self.showpaths('-srcIA', '1-ff00:0:112', '-sciondFromIA', + '-dstIA', '1-ff00:0:110', '-p', '-local', + '1-ff00:0:112,[127.0.0.1]') + else: + local_disp = local['docker']('inspect', + '-f', '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', + 'scion_disp_1-ff00_0_112') + local_disp = local_disp.strip() + out = self.tools_dc('exec_tester', '1-ff00_0_112', './bin/showpaths', + '-srcIA', '1-ff00:0:112', '-sciondFromIA', + '-dstIA', '1-ff00:0:110', '-p', '-local', + '1-ff00:0:112,[%s]' % local_disp) + + if 'Alive' not in out: + logger.error("Alive not found in output, output=%s", out) + return 1 + logger.info("successful") + +if __name__ == '__main__': + init_log() + Test.run() diff --git a/acceptance/sig_short_exp_time_acceptance/test b/acceptance/sig_short_exp_time_acceptance/test index c676119939..dd2c202660 100755 --- a/acceptance/sig_short_exp_time_acceptance/test +++ b/acceptance/sig_short_exp_time_acceptance/test @@ -1,6 +1,19 @@ #!/usr/bin/env python3 # 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. + import logging import os diff --git a/go/lib/sciond/pathprobe/BUILD.bazel b/go/lib/sciond/pathprobe/BUILD.bazel new file mode 100644 index 0000000000..4a3d46edd0 --- /dev/null +++ b/go/lib/sciond/pathprobe/BUILD.bazel @@ -0,0 +1,19 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["paths.go"], + importpath = "github.com/scionproto/scion/go/lib/sciond/pathprobe", + visibility = ["//visibility:public"], + deps = [ + "//go/lib/addr:go_default_library", + "//go/lib/common:go_default_library", + "//go/lib/log:go_default_library", + "//go/lib/sciond:go_default_library", + "//go/lib/scmp:go_default_library", + "//go/lib/snet:go_default_library", + "//go/lib/sock/reliable:go_default_library", + "//go/lib/spath:go_default_library", + "@org_golang_x_xerrors//:go_default_library", + ], +) diff --git a/go/lib/sciond/pathprobe/paths.go b/go/lib/sciond/pathprobe/paths.go new file mode 100644 index 0000000000..d87a526aff --- /dev/null +++ b/go/lib/sciond/pathprobe/paths.go @@ -0,0 +1,196 @@ +// 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 pathprobe + +import ( + "context" + "errors" + "fmt" + "sync" + + "golang.org/x/xerrors" + + "github.com/scionproto/scion/go/lib/addr" + "github.com/scionproto/scion/go/lib/common" + "github.com/scionproto/scion/go/lib/log" + "github.com/scionproto/scion/go/lib/sciond" + "github.com/scionproto/scion/go/lib/scmp" + "github.com/scionproto/scion/go/lib/snet" + "github.com/scionproto/scion/go/lib/sock/reliable" + "github.com/scionproto/scion/go/lib/spath" +) + +const ( + statusUnknown = "Unknown" + statusTimeout = "Timeout" + statusAlive = "Alive" + statusSCMP = "SCMP" +) + +// PathStatus indicates the state a path is in. +type PathStatus struct { + status string + additionalInfo string +} + +// Predefined path status +var ( + Unknown = PathStatus{status: statusUnknown} + Timeout = PathStatus{status: statusTimeout} + Alive = PathStatus{status: statusAlive} +) + +func (s PathStatus) String() string { + if s.additionalInfo == "" { + return s.status + } + return fmt.Sprintf("%s(%s)", s.status, s.additionalInfo) +} + +// PathProber can be used to get the status of a path. +type PathProber struct { + SrcIA addr.IA + DstIA addr.IA + Local snet.Addr +} + +// GetStatuses probes the paths and returns the statuses of the paths. The +// returned map is keyed with path.Path.FwdPath. +func (p PathProber) GetStatuses(ctx context.Context, + paths []sciond.PathReplyEntry) (map[string]PathStatus, error) { + + deadline, ok := ctx.Deadline() + if !ok { + return nil, common.NewBasicError("deadline required on ctx", nil) + } + // Check whether paths are alive. This is done by sending a packet + // with invalid address via the path. The border router at the destination + // is going to reply with SCMP error. Receiving the error means that + // the path is alive. + pathStatuses := make(map[string]PathStatus, len(paths)) + scmpH := scmpHandler{mtx: &sync.Mutex{}, statuses: pathStatuses} + network := snet.NewCustomNetworkWithPR(p.Local.IA, + &snet.DefaultPacketDispatcherService{ + Dispatcher: reliable.NewDispatcherService(""), + SCMPHandler: scmpH, + }, + nil, + ) + if err := snet.InitWithNetwork(network); err != nil { + return nil, common.NewBasicError("failed to initialize SNET", err) + } + snetConn, err := snet.ListenSCION("udp4", &p.Local) + if err != nil { + return nil, common.NewBasicError("listening failed", err) + } + scionConn := snetConn.(*snet.SCIONConn) + err = scionConn.SetReadDeadline(deadline) + if err != nil { + return nil, common.NewBasicError("failed to set deadline", err) + } + for _, path := range paths { + scmpH.setStatus(string(path.Path.FwdPath), Timeout) + p.sendTestPacket(scionConn, path) + } + for i := len(scmpH.statuses); i > 0; i-- { + err := p.receiveTestReply(scionConn) + if err != nil { + return nil, err + } + } + return scmpH.statuses, nil +} + +func (p PathProber) sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathReplyEntry) error { + sPath := spath.New(path.Path.FwdPath) + if err := sPath.InitOffsets(); err != nil { + return common.NewBasicError("unable to initialize path", err) + } + nextHop, err := path.HostInfo.Overlay() + if err != nil { + return common.NewBasicError("unable to get overlay info", err) + } + addr := &snet.Addr{ + IA: p.DstIA, + Host: &addr.AppAddr{ + L3: addr.HostSVCFromString("NONE"), + L4: addr.NewL4UDPInfo(0), + }, + NextHop: nextHop, + Path: sPath, + } + log.Debug("Sending test packet.", "path", path.Path.String()) + _, err = scionConn.WriteTo([]byte{}, addr) + if err != nil { + return common.NewBasicError("cannot send packet", err) + } + return nil +} + +func (p PathProber) receiveTestReply(scionConn *snet.SCIONConn) error { + b := make([]byte, 1500, 1500) + _, _, err := scionConn.ReadFromSCION(b) + if err == nil { + // We've got an actual reply instead of SCMP error. This should not happen. + return nil + } + if xerrors.Is(err, errBadHost) || xerrors.Is(err, errSCMP) { + return nil + } + if common.IsTimeoutErr(err) { + // Timeout expired before all replies were received. + return nil + } + return common.NewBasicError("failed to read packet", err) +} + +var errBadHost = errors.New("scmp: bad host") +var errSCMP = errors.New("scmp: other") + +type scmpHandler struct { + mtx *sync.Mutex + statuses map[string]PathStatus +} + +func (h scmpHandler) Handle(pkt *snet.SCIONPacket) error { + hdr, ok := pkt.L4Header.(*scmp.Hdr) + if ok { + path, err := h.path(pkt) + if err != nil { + return err + } + if hdr.Class == scmp.C_Routing && hdr.Type == scmp.T_R_BadHost { + h.setStatus(path, Alive) + return errBadHost + } + h.setStatus(path, PathStatus{status: statusSCMP, additionalInfo: hdr.String()}) + return errSCMP + } + return nil +} + +func (h scmpHandler) path(pkt *snet.SCIONPacket) (string, error) { + path := pkt.Path.Copy() + if err := path.Reverse(); err != nil { + return "", common.NewBasicError("unable to reverse path on received packet", err) + } + return string(path.Raw), nil +} + +func (h scmpHandler) setStatus(path string, status PathStatus) { + h.mtx.Lock() + defer h.mtx.Unlock() + h.statuses[path] = status +} diff --git a/go/protobuf/BUILD.bazel b/go/protobuf/BUILD.bazel new file mode 100644 index 0000000000..fe9a5b973d --- /dev/null +++ b/go/protobuf/BUILD.bazel @@ -0,0 +1,16 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "common.pb.go", + "conf_agent.pb.go", + "sig_mgmt.pb.go", + ], + importpath = "github.com/scionproto/scion/go/protobuf", + visibility = ["//visibility:public"], + deps = [ + "@com_github_golang_protobuf//proto:go_default_library", + "@org_golang_google_grpc//:go_default_library", + ], +) diff --git a/go/tools/showpaths/BUILD.bazel b/go/tools/showpaths/BUILD.bazel index caf2efbd07..d9958f91f3 100644 --- a/go/tools/showpaths/BUILD.bazel +++ b/go/tools/showpaths/BUILD.bazel @@ -8,14 +8,11 @@ go_library( visibility = ["//visibility:private"], deps = [ "//go/lib/addr:go_default_library", - "//go/lib/common:go_default_library", "//go/lib/env:go_default_library", "//go/lib/log:go_default_library", "//go/lib/sciond:go_default_library", - "//go/lib/scmp:go_default_library", + "//go/lib/sciond/pathprobe:go_default_library", "//go/lib/snet:go_default_library", - "//go/lib/sock/reliable:go_default_library", - "//go/lib/spath:go_default_library", ], ) diff --git a/go/tools/showpaths/paths.go b/go/tools/showpaths/paths.go index c517f821d7..f9f0718f96 100644 --- a/go/tools/showpaths/paths.go +++ b/go/tools/showpaths/paths.go @@ -19,19 +19,15 @@ import ( "context" "flag" "fmt" - "net" "os" "time" "github.com/scionproto/scion/go/lib/addr" - "github.com/scionproto/scion/go/lib/common" "github.com/scionproto/scion/go/lib/env" "github.com/scionproto/scion/go/lib/log" "github.com/scionproto/scion/go/lib/sciond" - "github.com/scionproto/scion/go/lib/scmp" + "github.com/scionproto/scion/go/lib/sciond/pathprobe" "github.com/scionproto/scion/go/lib/snet" - "github.com/scionproto/scion/go/lib/sock/reliable" - "github.com/scionproto/scion/go/lib/spath" ) var ( @@ -84,9 +80,18 @@ func main() { } fmt.Println("Available paths to", dstIA) - var pathStatuses map[string]string + var pathStatuses map[string]pathprobe.PathStatus if *status { - pathStatuses = getStatuses(reply.Entries) + ctx, cancelF := context.WithTimeout(context.Background(), *timeout) + pathStatuses, err = pathprobe.PathProber{ + Local: local, + SrcIA: srcIA, + DstIA: dstIA, + }.GetStatuses(ctx, reply.Entries) + cancelF() + if err != nil { + LogFatal("Failed to get status", "err", err) + } } for i, path := range reply.Entries { fmt.Printf("[%2d] %s", i, path.Path.String()) @@ -157,95 +162,3 @@ func LogFatal(msg string, a ...interface{}) { log.Crit(msg, a...) os.Exit(1) } - -func getStatuses(paths []sciond.PathReplyEntry) map[string]string { - // Check whether paths are alive. This is done by sending a packet - // with invalid address via the path. The border router at the destination - // is going to reply with SCMP error. Receiving the error means that - // the path is alive. - if err := snet.Init(srcIA, "", reliable.NewDispatcherService("")); err != nil { - LogFatal("Initializing SNET", "err", err) - } - snetConn, err := snet.ListenSCION("udp4", &local) - if err != nil { - LogFatal("Listening failed", "err", err) - } - scionConn := snetConn.(*snet.SCIONConn) - err = scionConn.SetReadDeadline(time.Now().Add(*timeout)) - if err != nil { - LogFatal("Cannot set deadline", "err", err) - } - pathStatuses := make(map[string]string) - for _, path := range paths { - sendTestPacket(scionConn, path) - pathStatuses[string(path.Path.FwdPath)] = "Timeout" - } - for i := len(pathStatuses); i > 0; i-- { - path, status := receiveTestReply(scionConn) - if path == nil { - break - } - if pathStatuses[*path] != "Timeout" { - // Two replies received for the same path. - pathStatuses[*path] = "Unknown" - continue - } - pathStatuses[*path] = status - } - return pathStatuses -} - -func sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathReplyEntry) { - sPath := spath.New(path.Path.FwdPath) - if err := sPath.InitOffsets(); err != nil { - LogFatal("Unable to initialize path", "err", err) - } - nextHop, err := path.HostInfo.Overlay() - if err != nil { - LogFatal("Cannot get overlay info", "err", err) - } - addr := &snet.Addr{ - IA: dstIA, - Host: &addr.AppAddr{ - L3: addr.HostSVCFromString("NONE"), - L4: addr.NewL4UDPInfo(0), - }, - NextHop: nextHop, - Path: sPath, - } - log.Debug("Sending test packet.", "path", path.Path.String()) - _, err = scionConn.WriteTo([]byte{}, addr) - if err != nil { - LogFatal("Cannot send packet", "err", err) - } -} - -func receiveTestReply(scionConn *snet.SCIONConn) (*string, string) { - b := make([]byte, 1500, 1500) - _, addr, err := scionConn.ReadFromSCION(b) - if addr == nil { - if basicErr, ok := err.(common.BasicError); ok { - if netErr, ok := basicErr.Err.(net.Error); ok && netErr.Timeout() { - // Timeout expired before all replies were received. - return nil, "" - } - } - if err != nil { - LogFatal("Cannot read packet", "err", err) - } - LogFatal("Packet without an address received", "err", err) - } - path := string(addr.Path.Raw) - if err == nil { - // We've got an actual reply instead of SCMP error. This should not happen. - return &path, "Unknown" - } - if opErr, ok := err.(*snet.OpError); ok { - if opErr.SCMP().Class == scmp.C_Routing && opErr.SCMP().Type == scmp.T_R_BadHost { - // Expected outcome. The peer complains about SvcNone being an invalid address. - return &path, "Alive" - } - } - // All other errors are just reported alongside the path. - return &path, err.Error() -} From a8f03836b26f689d305bbf28d0936441af555a77 Mon Sep 17 00:00:00 2001 From: Lukas Vogel Date: Tue, 10 Sep 2019 15:30:10 +0200 Subject: [PATCH 2/5] remove wrongly commited file --- go/protobuf/BUILD.bazel | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 go/protobuf/BUILD.bazel diff --git a/go/protobuf/BUILD.bazel b/go/protobuf/BUILD.bazel deleted file mode 100644 index fe9a5b973d..0000000000 --- a/go/protobuf/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") - -go_library( - name = "go_default_library", - srcs = [ - "common.pb.go", - "conf_agent.pb.go", - "sig_mgmt.pb.go", - ], - importpath = "github.com/scionproto/scion/go/protobuf", - visibility = ["//visibility:public"], - deps = [ - "@com_github_golang_protobuf//proto:go_default_library", - "@org_golang_google_grpc//:go_default_library", - ], -) From ed74a762f0fbca448abd64b9b804c42c2b995adf Mon Sep 17 00:00:00 2001 From: Lukas Vogel Date: Tue, 10 Sep 2019 16:50:40 +0200 Subject: [PATCH 3/5] r1 --- acceptance/showpaths_status_acceptance/test | 10 +- go/lib/sciond/pathprobe/paths.go | 113 +++++++++++--------- go/tools/showpaths/paths.go | 6 +- 3 files changed, 75 insertions(+), 54 deletions(-) diff --git a/acceptance/showpaths_status_acceptance/test b/acceptance/showpaths_status_acceptance/test index 7c937ac4e1..96f869e858 100755 --- a/acceptance/showpaths_status_acceptance/test +++ b/acceptance/showpaths_status_acceptance/test @@ -32,9 +32,11 @@ class Test(TestBase): Test that showpaths with the -p option works as intended. """ + class Base(CmdBase): pass + @Test.subcommand('setup') class TestSetup(Base): @@ -46,6 +48,7 @@ class TestSetup(Base): self.tools_dc('start', 'tester*') self.docker_status() + @Test.subcommand("run") class TestRun(Base): showpaths = local['./bin/showpaths'] @@ -57,9 +60,9 @@ class TestRun(Base): '-dstIA', '1-ff00:0:110', '-p', '-local', '1-ff00:0:112,[127.0.0.1]') else: - local_disp = local['docker']('inspect', - '-f', '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', - 'scion_disp_1-ff00_0_112') + local_disp = local['docker']('inspect', '-f', + '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', + 'scion_disp_1-ff00_0_112') local_disp = local_disp.strip() out = self.tools_dc('exec_tester', '1-ff00_0_112', './bin/showpaths', '-srcIA', '1-ff00:0:112', '-sciondFromIA', @@ -71,6 +74,7 @@ class TestRun(Base): return 1 logger.info("successful") + if __name__ == '__main__': init_log() Test.run() diff --git a/go/lib/sciond/pathprobe/paths.go b/go/lib/sciond/pathprobe/paths.go index d87a526aff..b2d9d24f7e 100644 --- a/go/lib/sciond/pathprobe/paths.go +++ b/go/lib/sciond/pathprobe/paths.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "sync" + "time" "golang.org/x/xerrors" @@ -32,44 +33,58 @@ import ( "github.com/scionproto/scion/go/lib/spath" ) +// StatusName defines the different states a path can be in. +type StatusName string + const ( - statusUnknown = "Unknown" - statusTimeout = "Timeout" - statusAlive = "Alive" - statusSCMP = "SCMP" + // StatusUnknown indicates that it is not clear what state the path is in. + StatusUnknown StatusName = "Unknown" + // StatusTimeout indicates that a reply did come back in time for the path. + StatusTimeout StatusName = "Timeout" + // StatusAlive indicates that the expected reply did come back in time. + StatusAlive StatusName = "Alive" + // StatusSCMP indicates that an unexpected SCMP packet came in the reply. + StatusSCMP StatusName = "SCMP" ) -// PathStatus indicates the state a path is in. -type PathStatus struct { - status string - additionalInfo string +// Status indicates the state a path is in. +type Status struct { + Status StatusName + AdditionalInfo string } // Predefined path status var ( - Unknown = PathStatus{status: statusUnknown} - Timeout = PathStatus{status: statusTimeout} - Alive = PathStatus{status: statusAlive} + unknown = Status{Status: StatusUnknown} + timeout = Status{Status: StatusTimeout} + alive = Status{Status: StatusAlive} ) -func (s PathStatus) String() string { - if s.additionalInfo == "" { - return s.status +func (s Status) String() string { + if s.AdditionalInfo == "" { + return string(s.Status) } - return fmt.Sprintf("%s(%s)", s.status, s.additionalInfo) + return fmt.Sprintf("%s(%s)", s.Status, s.AdditionalInfo) +} + +// PathKey is the mapping of a path reply entry to a key that is returned in +// GetStatuses. +func PathKey(path sciond.PathReplyEntry) string { + return string(path.Path.FwdPath) } -// PathProber can be used to get the status of a path. -type PathProber struct { - SrcIA addr.IA - DstIA addr.IA - Local snet.Addr +// Prober can be used to get the status of a path. +type Prober struct { + SrcIA addr.IA + DstIA addr.IA + Local snet.Addr + DispPath string } // GetStatuses probes the paths and returns the statuses of the paths. The // returned map is keyed with path.Path.FwdPath. -func (p PathProber) GetStatuses(ctx context.Context, - paths []sciond.PathReplyEntry) (map[string]PathStatus, error) { +func (p Prober) GetStatuses(ctx context.Context, + paths []sciond.PathReplyEntry) (map[string]Status, error) { deadline, ok := ctx.Deadline() if !ok { @@ -79,41 +94,43 @@ func (p PathProber) GetStatuses(ctx context.Context, // with invalid address via the path. The border router at the destination // is going to reply with SCMP error. Receiving the error means that // the path is alive. - pathStatuses := make(map[string]PathStatus, len(paths)) - scmpH := scmpHandler{mtx: &sync.Mutex{}, statuses: pathStatuses} + pathStatuses := make(map[string]Status, len(paths)) + scmpH := &scmpHandler{statuses: pathStatuses} network := snet.NewCustomNetworkWithPR(p.Local.IA, &snet.DefaultPacketDispatcherService{ - Dispatcher: reliable.NewDispatcherService(""), + Dispatcher: reliable.NewDispatcherService(p.DispPath), SCMPHandler: scmpH, }, nil, ) - if err := snet.InitWithNetwork(network); err != nil { - return nil, common.NewBasicError("failed to initialize SNET", err) - } - snetConn, err := snet.ListenSCION("udp4", &p.Local) + snetConn, err := network.ListenSCION("udp4", &p.Local, deadline.Sub(time.Now())) if err != nil { return nil, common.NewBasicError("listening failed", err) } - scionConn := snetConn.(*snet.SCIONConn) - err = scionConn.SetReadDeadline(deadline) - if err != nil { - return nil, common.NewBasicError("failed to set deadline", err) - } + defer snetConn.Close() + var sendErrors common.MultiError for _, path := range paths { - scmpH.setStatus(string(path.Path.FwdPath), Timeout) - p.sendTestPacket(scionConn, path) + scmpH.setStatus(PathKey(path), timeout) + if err := p.send(snetConn, path); err != nil { + sendErrors = append(sendErrors, err) + } + } + if err := sendErrors.ToError(); err != nil { + return nil, err } + var receiveErrors common.MultiError for i := len(scmpH.statuses); i > 0; i-- { - err := p.receiveTestReply(scionConn) - if err != nil { - return nil, err + if err := p.receive(snetConn); err != nil { + receiveErrors = append(receiveErrors, err) } } + if err := receiveErrors.ToError(); err != nil { + return nil, err + } return scmpH.statuses, nil } -func (p PathProber) sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathReplyEntry) error { +func (p Prober) send(scionConn snet.Conn, path sciond.PathReplyEntry) error { sPath := spath.New(path.Path.FwdPath) if err := sPath.InitOffsets(); err != nil { return common.NewBasicError("unable to initialize path", err) @@ -139,7 +156,7 @@ func (p PathProber) sendTestPacket(scionConn *snet.SCIONConn, path sciond.PathRe return nil } -func (p PathProber) receiveTestReply(scionConn *snet.SCIONConn) error { +func (p Prober) receive(scionConn snet.Conn) error { b := make([]byte, 1500, 1500) _, _, err := scionConn.ReadFromSCION(b) if err == nil { @@ -160,11 +177,11 @@ var errBadHost = errors.New("scmp: bad host") var errSCMP = errors.New("scmp: other") type scmpHandler struct { - mtx *sync.Mutex - statuses map[string]PathStatus + mtx sync.Mutex + statuses map[string]Status } -func (h scmpHandler) Handle(pkt *snet.SCIONPacket) error { +func (h *scmpHandler) Handle(pkt *snet.SCIONPacket) error { hdr, ok := pkt.L4Header.(*scmp.Hdr) if ok { path, err := h.path(pkt) @@ -172,16 +189,16 @@ func (h scmpHandler) Handle(pkt *snet.SCIONPacket) error { return err } if hdr.Class == scmp.C_Routing && hdr.Type == scmp.T_R_BadHost { - h.setStatus(path, Alive) + h.setStatus(path, alive) return errBadHost } - h.setStatus(path, PathStatus{status: statusSCMP, additionalInfo: hdr.String()}) + h.setStatus(path, Status{Status: StatusSCMP, AdditionalInfo: hdr.String()}) return errSCMP } return nil } -func (h scmpHandler) path(pkt *snet.SCIONPacket) (string, error) { +func (h *scmpHandler) path(pkt *snet.SCIONPacket) (string, error) { path := pkt.Path.Copy() if err := path.Reverse(); err != nil { return "", common.NewBasicError("unable to reverse path on received packet", err) @@ -189,7 +206,7 @@ func (h scmpHandler) path(pkt *snet.SCIONPacket) (string, error) { return string(path.Raw), nil } -func (h scmpHandler) setStatus(path string, status PathStatus) { +func (h *scmpHandler) setStatus(path string, status Status) { h.mtx.Lock() defer h.mtx.Unlock() h.statuses[path] = status diff --git a/go/tools/showpaths/paths.go b/go/tools/showpaths/paths.go index f9f0718f96..ae0188a3a4 100644 --- a/go/tools/showpaths/paths.go +++ b/go/tools/showpaths/paths.go @@ -80,10 +80,10 @@ func main() { } fmt.Println("Available paths to", dstIA) - var pathStatuses map[string]pathprobe.PathStatus + var pathStatuses map[string]pathprobe.Status if *status { ctx, cancelF := context.WithTimeout(context.Background(), *timeout) - pathStatuses, err = pathprobe.PathProber{ + pathStatuses, err = pathprobe.Prober{ Local: local, SrcIA: srcIA, DstIA: dstIA, @@ -100,7 +100,7 @@ func main() { time.Until(path.Path.Expiry()).Truncate(time.Second)) } if *status { - fmt.Printf(" Status: %s", pathStatuses[string(path.Path.FwdPath)]) + fmt.Printf(" Status: %s", pathStatuses[pathprobe.PathKey(path)]) } fmt.Printf("\n") } From 5d61b89f8cc2b00cc3a375ebdc91a975ef3e8f82 Mon Sep 17 00:00:00 2001 From: Lukas Vogel Date: Tue, 10 Sep 2019 17:05:53 +0200 Subject: [PATCH 4/5] r2 --- go/lib/sciond/pathprobe/paths.go | 1 - go/tools/showpaths/paths.go | 1 - 2 files changed, 2 deletions(-) diff --git a/go/lib/sciond/pathprobe/paths.go b/go/lib/sciond/pathprobe/paths.go index b2d9d24f7e..8d3efb20d8 100644 --- a/go/lib/sciond/pathprobe/paths.go +++ b/go/lib/sciond/pathprobe/paths.go @@ -75,7 +75,6 @@ func PathKey(path sciond.PathReplyEntry) string { // Prober can be used to get the status of a path. type Prober struct { - SrcIA addr.IA DstIA addr.IA Local snet.Addr DispPath string diff --git a/go/tools/showpaths/paths.go b/go/tools/showpaths/paths.go index ae0188a3a4..0616ad6609 100644 --- a/go/tools/showpaths/paths.go +++ b/go/tools/showpaths/paths.go @@ -85,7 +85,6 @@ func main() { ctx, cancelF := context.WithTimeout(context.Background(), *timeout) pathStatuses, err = pathprobe.Prober{ Local: local, - SrcIA: srcIA, DstIA: dstIA, }.GetStatuses(ctx, reply.Entries) cancelF() From 6322a64a2ea87cf6f0624c071c5987941848cfa0 Mon Sep 17 00:00:00 2001 From: Lukas Vogel Date: Wed, 11 Sep 2019 07:42:54 +0200 Subject: [PATCH 5/5] use yaml to find disp addr --- acceptance/showpaths_status_acceptance/test | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/acceptance/showpaths_status_acceptance/test b/acceptance/showpaths_status_acceptance/test index 96f869e858..00b97ff86b 100755 --- a/acceptance/showpaths_status_acceptance/test +++ b/acceptance/showpaths_status_acceptance/test @@ -19,9 +19,10 @@ import logging from plumbum import local + from acceptance.common.log import LogExec, init_log from acceptance.common.base import CmdBase, TestBase, set_name - +from lib.util import load_yaml_file set_name(__file__) logger = logging.getLogger(__name__) @@ -60,10 +61,9 @@ class TestRun(Base): '-dstIA', '1-ff00:0:110', '-p', '-local', '1-ff00:0:112,[127.0.0.1]') else: - local_disp = local['docker']('inspect', '-f', - '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}', - 'scion_disp_1-ff00_0_112') - local_disp = local_disp.strip() + dcFile = load_yaml_file('gen/scion-dc.yml') + networks = dcFile['services']['scion_disp_1-ff00_0_112']['networks'] + local_disp = next(iter(networks.items()))[1]['ipv4_address'] out = self.tools_dc('exec_tester', '1-ff00_0_112', './bin/showpaths', '-srcIA', '1-ff00:0:112', '-sciondFromIA', '-dstIA', '1-ff00:0:110', '-p', '-local',