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

cmd/scion: add json/yaml format to ping and traceroute #4287

Merged
merged 25 commits into from
Nov 28, 2022
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 39 additions & 25 deletions private/path/pathpol/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,33 +83,14 @@ func (s *Sequence) Eval(paths []snet.Path) []snet.Path {
}
result := []snet.Path{}
for _, path := range paths {
var desc string
ifaces := path.Metadata().Interfaces
switch {
// Path should contain even number of interfaces. 1 for source AS,
// 1 for destination AS and 2 per each intermediate AS. Invalid paths should
// not occur but if they do let's ignore them.
case len(ifaces)%2 != 0:
log.Error("Invalid path with even number of hops", "path", path)
desc, err := GetSequence(path)
if desc != "" {
desc = desc + " "
}
if err != nil {
log.Error("get sequence from path", "err", err)
continue
// Empty paths are special cased.
case len(ifaces) == 0:
desc = ""
default:
// Turn the path into a string. For each AS on the path there will be
// one element in form <IA>#<inbound-interface>,<outbound-interface>,
// e.g. 64-ff00:0:112#3,5. For the source AS, the inbound interface will be
// zero. For destination AS, outbound interface will be zero.

hops := make([]string, 0, len(ifaces)/2+1)
hops = append(hops, hop(ifaces[0].IA, 0, ifaces[0].ID))
for i := 1; i < len(ifaces)-1; i += 2 {
hops = append(hops, hop(ifaces[i].IA, ifaces[i].ID, ifaces[i+1].ID))
}
hops = append(hops, hop(ifaces[len(ifaces)-1].IA, ifaces[len(ifaces)-1].ID, 0))
desc = strings.Join(hops, " ") + " "
}

// Check whether the string matches the sequence regexp.
if s.re.MatchString(desc) {
result = append(result, path)
Expand Down Expand Up @@ -315,3 +296,36 @@ func (l *sequenceListener) ExitIFace(c *sequence.IFaceContext) {
func hop(ia addr.IA, ingress, egress common.IFIDType) string {
return fmt.Sprintf("%s#%d,%d", ia, ingress, egress)
}

// GetSequence constructs the sequence string from snet path
// output format:
//
// 1-ff00:0:133#42 1-ff00:0:120#2,1 1-ff00:0:110#21
func GetSequence(path snet.Path) (string, error) {
var desc string
ifaces := path.Metadata().Interfaces
switch {
// Path should contain even number of interfaces. 1 for source AS,
// 1 for destination AS and 2 per each intermediate AS. Invalid paths should
// not occur but if they do let's ignore them.
case len(ifaces)%2 != 0:
return "", serrors.New("Invalid path with odd number of hops", "path", path)
// Empty paths are special cased.
case len(ifaces) == 0:
desc = ""
default:
// Turn the path into a string. For each AS on the path there will be
// one element in form <IA>#<inbound-interface>,<outbound-interface>,
// e.g. 64-ff00:0:112#3,5. For the source AS, the inbound interface will be
// zero. For destination AS, outbound interface will be zero.

hops := make([]string, 0, len(ifaces)/2+1)
hops = append(hops, hop(ifaces[0].IA, 0, ifaces[0].ID))
for i := 1; i < len(ifaces)-1; i += 2 {
hops = append(hops, hop(ifaces[i].IA, ifaces[i].ID, ifaces[i+1].ID))
}
hops = append(hops, hop(ifaces[len(ifaces)-1].IA, ifaces[len(ifaces)-1].ID, 0))
desc = strings.Join(hops, " ")
}
return desc, nil
}
4 changes: 4 additions & 0 deletions scion/cmd/scion/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go_library(
name = "go_default_library",
srcs = [
"address.go",
"common.go",
"gendocs.go",
"main.go",
"observability.go",
Expand All @@ -18,6 +19,7 @@ go_library(
"//pkg/addr:go_default_library",
"//pkg/daemon:go_default_library",
"//pkg/log:go_default_library",
"//pkg/private/common:go_default_library",
"//pkg/private/serrors:go_default_library",
"//pkg/snet:go_default_library",
"//pkg/snet/addrutil:go_default_library",
Expand All @@ -28,6 +30,7 @@ go_library(
"//private/app/flag:go_default_library",
"//private/app/path:go_default_library",
"//private/env:go_default_library",
"//private/path/pathpol:go_default_library",
"//private/topology:go_default_library",
"//private/tracing:go_default_library",
"//scion/ping:go_default_library",
Expand All @@ -36,6 +39,7 @@ go_library(
"@com_github_opentracing_opentracing_go//:go_default_library",
"@com_github_spf13_cobra//:go_default_library",
"@com_github_spf13_cobra//doc:go_default_library",
"@in_gopkg_yaml_v2//:go_default_library",
],
)

Expand Down
74 changes: 74 additions & 0 deletions scion/cmd/scion/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright 2022 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 main

import (
"fmt"
"io"
"net"

"github.com/scionproto/scion/pkg/addr"
"github.com/scionproto/scion/pkg/private/common"
"github.com/scionproto/scion/pkg/private/serrors"
"github.com/scionproto/scion/pkg/snet"
)

// Path defines the base model for the `ping` and `traceroute` result path
type Path struct {
// Hex-string representing the paths fingerprint.
Fingerprint string `json:"fingerprint" yaml:"fingerprint"`
Hops []Hop `json:"hops" yaml:"hops"`
Sequence string `json:"sequence" yaml:"sequence"`

LocalIP net.IP `json:"local_ip,omitempty" yaml:"local_ip,omitempty"`

// The internal UDP/IP underlay address of the SCION router that forwards traffic for this path.
NextHop string `json:"next_hop" yaml:"next_hop"`
}

// Hop represents an hop on the path.
type Hop struct {
ID common.IFIDType `json:"interface" yaml:"interface"`
IA addr.IA `json:"isd_as" yaml:"isd_as"`
}

// getHops constructs a list of snet path interfaces from an snet path
func getHops(path snet.Path) []Hop {
ifaces := path.Metadata().Interfaces
var hops []Hop
if len(ifaces) == 0 {
return hops
}
for i := range ifaces {
intf := ifaces[i]
hops = append(hops, Hop{IA: intf.IA, ID: intf.ID})
}
return hops
}

// getPrintf returns a printf function for the "human" formatting flag and an empty one for machine
// readable format flags
func getPrintf(output string, writer io.Writer) (func(format string, ctx ...interface{}), error) {
switch output {
case "human":
return func(format string, ctx ...interface{}) {
fmt.Fprintf(writer, format, ctx...)
}, nil
case "yaml", "json":
return func(format string, ctx ...interface{}) {}, nil
default:
return nil, serrors.New("format not supported", "format", output)
}
}
Loading