Skip to content

Commit

Permalink
scmp/showpaths: remove local address flags (#3691)
Browse files Browse the repository at this point in the history
scmp: determine the local address automatically
   - The local IA is obtained from the sciond connection (i.e. it is determined by the `-sciond` parameter).
   - The local IP address is determined by a routing table lookup (by creating a temporary, connected UDP socket), based on the next hop address.
     The `-local` flag now allows to override this IP, to e.g. choose a specific network interface.

showpaths:
   - remove the redundant `-srcIA` parameter (was only used to check that it was entered correctly)
   - the local address for the health check is automatically determined, as described above. Unlike in the `scmp` case where the next hop of the selected path is used, here we determine the local IP based on a routing table lookup to the IP of address of an arbitrary infrastructure service, as this avoids restructuring the path prober too much (would have to create a separate connection for each path). This is not a fully general approach, but is intended as a "good enough" solution until `snet` supports wildcard addresses directly.
      The `-local` flag now allows to override this IP, to e.g. choose a specific network interface.

Other changes:
* fix binary integration tests setup; `bin_wrapper.sh` exit code was always 0, hiding failing test runs
  • Loading branch information
matzf committed Mar 24, 2020
1 parent 103b370 commit 64e5ff9
Show file tree
Hide file tree
Showing 16 changed files with 184 additions and 70 deletions.
11 changes: 4 additions & 7 deletions acceptance/showpaths_status_acceptance/test
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,8 @@ class TestRun(Base):
def main(self):
sciond_file = load_sciond_file('gen/sciond_addresses.json')
if self.no_docker:
out = self.showpaths('-srcIA', '1-ff00:0:112',
'-sciond', "[%s]:%d" % (sciond_file["1-ff00:0:112"], 30255),
'-dstIA', '1-ff00:0:110', '-p', '-local',
'1-ff00:0:112,[127.0.0.1]')
out = self.showpaths('-sciond', "[%s]:%d" % (sciond_file["1-ff00:0:112"], 30255),
'-dstIA', '1-ff00:0:110', '-p')
else:
dcFile = load_yaml_file('gen/scion-dc.yml')
networks = dcFile['services']['scion_disp_cs1-ff00_0_112-1']['networks']
Expand All @@ -75,9 +73,8 @@ class TestRun(Base):
local_disp = disp_net['ipv6_address']
out = self.tools_dc('exec_tester', '1-ff00_0_112', './bin/showpaths',
'-sciond', "[%s]:%d" % (sciond_file["1-ff00:0:112"], 30255),
'-srcIA', '1-ff00:0:112',
'-dstIA', '1-ff00:0:110', '-p', '-local',
'1-ff00:0:112,[%s]' % local_disp)
'-dstIA', '1-ff00:0:110', '-p',
'-local', local_disp)

if 'Alive' not in out:
logger.error("Alive not found in output, output=%s", out)
Expand Down
4 changes: 4 additions & 0 deletions go/lib/sciond/fake/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ func (c connector) adapter(paths []*Path) []snet.Path {
return snetPaths
}

func (c connector) LocalIA(ctx context.Context) (addr.IA, error) {
panic("not implemented")
}

func (c connector) ASInfo(ctx context.Context, ia addr.IA) (*sciond.ASInfoReply, error) {
panic("not implemented")
}
Expand Down
15 changes: 15 additions & 0 deletions go/lib/sciond/mock_sciond/sciond.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 8 additions & 6 deletions go/lib/sciond/pathprobe/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"errors"
"fmt"
"net"
"sync"

"github.com/scionproto/scion/go/lib/addr"
Expand Down Expand Up @@ -87,9 +88,9 @@ func FilterEmptyPaths(paths []snet.Path) []snet.Path {

// Prober can be used to get the status of a path.
type Prober struct {
DstIA addr.IA
Local snet.UDPAddr
DispPath string
DstIA addr.IA
LocalIA addr.IA
LocalIP net.IP
}

// GetStatuses probes the paths and returns the statuses of the paths. The
Expand All @@ -102,19 +103,20 @@ func (p Prober) GetStatuses(ctx context.Context,
if !ok {
return nil, serrors.New("deadline required on ctx")
}

// 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]Status, len(paths))
scmpH := &scmpHandler{statuses: pathStatuses}
network := snet.NewCustomNetworkWithPR(p.Local.IA,
network := snet.NewCustomNetworkWithPR(p.LocalIA,
&snet.DefaultPacketDispatcherService{
Dispatcher: reliable.NewDispatcher(p.DispPath),
Dispatcher: reliable.NewDispatcher(""),
SCMPHandler: scmpH,
},
)
snetConn, err := network.Listen(ctx, "udp", p.Local.Host, addr.SvcNone)
snetConn, err := network.Listen(ctx, "udp", &net.UDPAddr{IP: p.LocalIP}, addr.SvcNone)
if err != nil {
return nil, common.NewBasicError("listening failed", err)
}
Expand Down
18 changes: 14 additions & 4 deletions go/lib/sciond/sciond.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ func (s *service) Connect(ctx context.Context) (Connector, error) {
return newConn(ctx, s.path)
}

// A Connector is used to query SCIOND. The connector maintains an internal
// cache for interface, service and AS information. All connector methods block until either
// an error occurs, or the method successfully returns.
// A Connector is used to query SCIOND. All connector methods block until
// either an error occurs, or the method successfully returns.
type Connector interface {
// Paths requests from SCIOND a set of end to end paths between src and
// LocalIA requests from SCIOND the local ISD-AS number.
LocalIA(ctx context.Context) (addr.IA, error)
// Paths requests from SCIOND a set of end to end paths between the source and destination.
Paths(ctx context.Context, dst, src addr.IA, f PathReqFlags) ([]snet.Path, error)
// ASInfo requests from SCIOND information about AS ia.
ASInfo(ctx context.Context, ia addr.IA) (*ASInfoReply, error)
Expand Down Expand Up @@ -177,6 +178,15 @@ func (c *conn) Paths(ctx context.Context, dst, src addr.IA,
return pathReplyToPaths(reply.PathReply, dst)
}

func (c *conn) LocalIA(ctx context.Context) (addr.IA, error) {
asInfo, err := c.ASInfo(ctx, addr.IA{})
if err != nil {
return addr.IA{}, err
}
ia := asInfo.Entries[0].RawIsdas.IA()
return ia, nil
}

func (c *conn) ASInfo(ctx context.Context, ia addr.IA) (*ASInfoReply, error) {
conn, err := c.connect(ctx)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions go/lib/snet/addrutil/addrutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,15 @@ func GetPath(svc addr.HostSVC, ps *seg.PathSegment, topoProv topology.Provider)
}
return &snet.SVCAddr{IA: ps.FirstIA(), Path: p, NextHop: overlayNextHop, SVC: svc}, nil
}

// ResolveLocal returns the local IP address used for traffic destined to dst.
func ResolveLocal(dst net.IP) (net.IP, error) {
udpAddr := net.UDPAddr{IP: dst, Port: 1}
udpConn, err := net.DialUDP(udpAddr.Network(), nil, &udpAddr)
if err != nil {
return nil, err
}
defer udpConn.Close()
srcIP := udpConn.LocalAddr().(*net.UDPAddr).IP
return srcIP, nil
}
1 change: 1 addition & 0 deletions go/tools/scmp/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
"//go/lib/addr:go_default_library",
"//go/lib/sciond:go_default_library",
"//go/lib/snet:go_default_library",
"//go/lib/snet/addrutil:go_default_library",
"//go/lib/sock/reliable:go_default_library",
"//go/tools/scmp/cmn:go_default_library",
"//go/tools/scmp/echo:go_default_library",
Expand Down
2 changes: 1 addition & 1 deletion go/tools/scmp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Example usage of scmp tool for echo (a.k.a. ping):

```bash
make
./bin/scmp -local 1-ff00:0:133,[127.0.0.75] -remote 2-ff00:0:222,[127.0.0.228]
./bin/scmp echo -remote 2-ff00:0:222,[127.0.0.228]
```

You can run scmp tool in Interactive mode with -i flag to be able to choose
Expand Down
17 changes: 11 additions & 6 deletions go/tools/scmp/cmn/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ var (
Interactive bool
Interval time.Duration
Timeout time.Duration
Local snet.UDPAddr
Remote snet.UDPAddr
localIP string
)

var (
LocalIA addr.IA
LocalIP net.IP
Conn net.PacketConn
Mtu uint16
PathEntry snet.Path
Expand All @@ -75,7 +77,7 @@ func init() {
flag.DurationVar(&Interval, "interval", DefaultInterval, "time between packets (echo only)")
flag.DurationVar(&Timeout, "timeout", DefaultTimeout, "timeout per packet")
flag.UintVar(&Count, "c", 0, "Total number of packet to send (echo only). Maximum value 65535")
flag.Var(&Local, "local", "(Mandatory) address to listen on")
flag.StringVar(&localIP, "local", "", "(Optional) IP address to listen on")
flag.Var(&Remote, "remote", "(Mandatory for clients) address to connect to")
flag.Usage = scmpUsage
Stats = &ScmpStats{}
Expand Down Expand Up @@ -123,8 +125,11 @@ func ParseFlags(version *bool) string {
}

func ValidateFlags() {
if Local.Host == nil {
Fatal("Invalid local address")
if localIP != "" {
LocalIP = net.ParseIP(localIP)
if LocalIP == nil {
Fatal("Invalid local address")
}
}
if Remote.Host == nil {
Fatal("Invalid remote address")
Expand All @@ -151,9 +156,9 @@ func NewSCMPPkt(t scmp.Type, info scmp.Info, ext common.Extension) *spkt.ScnPkt
}
pkt := &spkt.ScnPkt{
DstIA: Remote.IA,
SrcIA: Local.IA,
SrcIA: LocalIA,
DstHost: addr.HostFromIP(Remote.Host.IP),
SrcHost: addr.HostFromIP(Local.Host.IP),
SrcHost: addr.HostFromIP(LocalIP),
Path: Remote.Path,
HBHExt: exts,
L4: scmpHdr,
Expand Down
58 changes: 40 additions & 18 deletions go/tools/scmp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ import (
"context"
"flag"
"fmt"
"net"
"os"
"strconv"
"time"

"github.com/scionproto/scion/go/lib/addr"
"github.com/scionproto/scion/go/lib/sciond"
"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/tools/scmp/cmn"
"github.com/scionproto/scion/go/tools/scmp/echo"
Expand All @@ -38,8 +40,9 @@ var (
sciondAddr = flag.String("sciond", sciond.DefaultSCIONDAddress, "SCIOND address")
dispatcher = flag.String("dispatcher", reliable.DefaultDispPath, "Path to dispatcher socket")
refresh = flag.Bool("refresh", false, "Set refresh flag for SCIOND path request")
sdConn sciond.Connector
version = flag.Bool("version", false, "Output version information and exit.")
sdConn sciond.Connector
localMtu uint16
)

func main() {
Expand All @@ -54,25 +57,34 @@ func main() {
if err != nil {
cmn.Fatal("Failed to connect to SCIOND: %v\n", err)
}
// Connect to the dispatcher
dispatcherService := reliable.NewDispatcher(*dispatcher)
cmn.Conn, _, err = dispatcherService.Register(context.Background(), cmn.Local.IA,
cmn.Local.Host, addr.SvcNone)
if err != nil {
cmn.Fatal("Unable to register with the dispatcher addr=%s\nerr=%v", cmn.Local, err)
}
defer cmn.Conn.Close()

setLocalASInfo()

// If remote is not in local AS, we need a path!
var pathStr string
if !cmn.Remote.IA.Equal(cmn.Local.IA) {
if !cmn.Remote.IA.Equal(cmn.LocalIA) {
setPathAndMtu()
pathStr = fmt.Sprintf("%s", cmn.PathEntry)
if cmn.LocalIP == nil {
cmn.LocalIP = resolveLocalIP(cmn.Remote.NextHop.IP)
}
} else {
cmn.Mtu = setLocalMtu()
cmn.Mtu = localMtu
if cmn.LocalIP == nil {
cmn.LocalIP = resolveLocalIP(cmn.Remote.Host.IP)
}
}
fmt.Printf("Using path:\n %s\n", pathStr)

// Connect to the dispatcher
dispatcherService := reliable.NewDispatcher(*dispatcher)
cmn.Conn, _, err = dispatcherService.Register(context.Background(), cmn.LocalIA,
&net.UDPAddr{IP: cmn.LocalIP}, addr.SvcNone)
if err != nil {
cmn.Fatal("Unable to register with the dispatcher addr=%s\nerr=%v", cmn.LocalIP, err)
}
defer cmn.Conn.Close()

ret := doCommand(cmd)
os.Exit(ret)
}
Expand All @@ -98,7 +110,7 @@ func doCommand(cmd string) int {
}

func choosePath() snet.Path {
paths, err := sdConn.Paths(context.Background(), cmn.Remote.IA, cmn.Local.IA,
paths, err := sdConn.Paths(context.Background(), cmn.Remote.IA, cmn.LocalIA,
sciond.PathReqFlags{Refresh: *refresh})
if err != nil {
cmn.Fatal("Failed to retrieve paths from SCIOND: %v\n", err)
Expand Down Expand Up @@ -136,12 +148,22 @@ func setPathAndMtu() {
cmn.Mtu = path.MTU()
}

func setLocalMtu() uint16 {
// Use local AS MTU when we have no path
reply, err := sdConn.ASInfo(context.Background(), addr.IA{})
// setLocalASInfo queries the local AS information from SCIOND; sets cmn.LocalIA and localMtu.
func setLocalASInfo() {
asInfo, err := sdConn.ASInfo(context.Background(), addr.IA{})
if err != nil {
cmn.Fatal("Failed to query local IA from SCIOND: %v\n", err)
}
e0 := asInfo.Entries[0]
cmn.LocalIA = e0.RawIsdas.IA()
localMtu = e0.Mtu
}

// resolveLocalIP returns the src IP used for traffic destined to dst
func resolveLocalIP(dst net.IP) net.IP {
srcIP, err := addrutil.ResolveLocal(dst)
if err != nil {
cmn.Fatal("Unable to request AS info to sciond")
cmn.Fatal("Failed to determine local IP: %v\n", err)
}
// XXX We expect a single entry in the reply
return reply.Entries[0].Mtu
return srcIP
}
12 changes: 10 additions & 2 deletions go/tools/scmp/scmp_integration/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,16 @@ func realMain() int {
defer log.HandlePanic()
defer log.Flush()

cmnArgs := []string{"-timeout", "4s", "-local", integration.SrcAddrPattern,
"-remote", integration.DstAddrPattern}
cmnArgs := []string{
"-timeout", "4s",
"-sciond", integration.SCIOND,
"-remote", integration.DstAddrPattern,
}
if *integration.Docker {
cmnArgs = append(cmnArgs,
"-local", integration.SrcHostReplace,
)
}

testCases := []struct {
Name string
Expand Down
2 changes: 1 addition & 1 deletion go/tools/scmp/traceroute/traceroute.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func prettyPrint(pkt *spkt.ScnPkt, info *scmp.InfoTraceRoute, rtt time.Duration)
// hopPktOff returns HopF offset relative to the packet
func hopPktOff(offset int) uint8 {
off := spkt.CmnHdrLen +
spkt.AddrHdrLen(addr.HostFromIP(cmn.Local.Host.IP), addr.HostFromIP(cmn.Remote.Host.IP)) +
spkt.AddrHdrLen(addr.HostFromIP(cmn.LocalIP), addr.HostFromIP(cmn.Remote.Host.IP)) +
offset
return uint8(off / common.LineLen)
}
Expand Down
1 change: 1 addition & 0 deletions go/tools/showpaths/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ go_library(
"//go/lib/sciond/pathprobe:go_default_library",
"//go/lib/serrors:go_default_library",
"//go/lib/snet:go_default_library",
"//go/lib/snet/addrutil:go_default_library",
],
)

Expand Down
7 changes: 3 additions & 4 deletions go/tools/showpaths/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@ Then, run:

```bash
make
./bin/showpaths -dstIA 2-ff00:0:222 -srcIA 1-ff00:0:133
./bin/showpaths -dstIA 2-ff00:0:222
```

Alternatively, you can also run the application using:

```bash
go run paths.go -dstIA 2-ff00:0:222 -srcIA 1-ff00:0:133
go run paths.go -dstIA 2-ff00:0:222
```

In the examples above, the application will display the paths between 1-ff00:0:133 and
2-ff00:0:222.
In the examples above, the application will display the paths between the local AS and 2-ff00:0:222.

For complete options:

Expand Down
Loading

0 comments on commit 64e5ff9

Please sign in to comment.