Skip to content

Commit

Permalink
Add method to filter segments with a path policy (#3072)
Browse files Browse the repository at this point in the history
This method pre-filters a list of segments with a given path policy.
Note that filtering with sequences is not easily possible since we do not know where a segment would fit into the sequence (up, core, down).
A possibility to add sequence filtering for segments would be to split the sequence into up, core, and down parts.

Note that filtering with ACLs should only be done on core segments.
Up and down segments contain peering interfaces which might be used in the final path, eventhough the ACL would filter it.

Fixes #2820
  • Loading branch information
lukedirtwalker authored Sep 3, 2019
1 parent e09809d commit 2bbb531
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 7 deletions.
3 changes: 1 addition & 2 deletions go/lib/pathmgr/pathmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,7 @@ type pathWrap struct {
*spathmeta.AppPath
}

func (p pathWrap) Key() string { return p.AppPath.Key().String() }
func (p pathWrap) IsPartial() bool { return false }
func (p pathWrap) Key() string { return p.AppPath.Key().String() }
func (p pathWrap) Interfaces() []pathpol.PathInterface {
intfs := make([]pathpol.PathInterface, 0, len(p.Entry.Path.Interfaces))
for _, intf := range p.Entry.Path.Interfaces {
Expand Down
2 changes: 0 additions & 2 deletions go/lib/pathpol/pathset.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ type PathSet map[string]Path
type Path interface {
// Interfaces returns all the interfaces of this path.
Interfaces() []PathInterface
// IsPartial returns whether this is a partial path.
IsPartial() bool
// Returns a string that uniquely identifies this path.
Key() string
}
Expand Down
2 changes: 0 additions & 2 deletions go/lib/pathpol/policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -822,8 +822,6 @@ func (p *testPath) Interfaces() []PathInterface {
return p.interfaces
}

func (p *testPath) IsPartial() bool { return false }

func (p *testPath) Key() string { return p.key }

type testPathIntf struct {
Expand Down
31 changes: 31 additions & 0 deletions go/path_srv/internal/segutil/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = ["filter.go"],
importpath = "github.com/scionproto/scion/go/path_srv/internal/segutil",
visibility = ["//go/path_srv:__subpackages__"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/ctrl/seg:go_default_library",
"//go/lib/pathpol:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["filter_test.go"],
embed = [":go_default_library"],
deps = [
"//go/lib/addr:go_default_library",
"//go/lib/common:go_default_library",
"//go/lib/ctrl/seg:go_default_library",
"//go/lib/pathpol:go_default_library",
"//go/lib/xtest:go_default_library",
"//go/lib/xtest/graph:go_default_library",
"@com_github_golang_mock//gomock:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
)
116 changes: 116 additions & 0 deletions go/path_srv/internal/segutil/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// 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 segutil

import (
"fmt"
"strings"

"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/pathpol"
)

type Direction int

const (
ReverseConsDir Direction = iota
ConsDir
)

// Filter filters the given segments with the policy. Dir indicates the
// direction of intended usage of the segments. For example up and core segments
// are most often used in reverse construction dir. The direction parameter is
// relevant for the sequence matching of policies. Note that order of segs is
// not preserved.
// NOTE: This function should only be applied on core segments, otherwise the PS
// might filter segments that could still have been used in a final path,
// because of peering links.
func Filter(segs seg.Segments, policy *pathpol.Policy, dir Direction) seg.Segments {
if policy == nil {
return segs
}
// The sequence filter doesn't work for segments, therefore a policy
// without sequence is used.
actPolicy := pathpol.NewPolicy(policy.Name, policy.ACL, nil, policy.Options)
return psToSegs(actPolicy.Act(segsToPs(segs, dir)))
}

func segsToPs(segs seg.Segments, dir Direction) pathpol.PathSet {
ps := make(pathpol.PathSet, len(segs))
for _, seg := range segs {
sw := wrap(seg, dir)
ps[sw.Key()] = sw
}
return ps
}

func psToSegs(ps pathpol.PathSet) seg.Segments {
segs := make(seg.Segments, 0, len(ps))
for _, sw := range ps {
seg := sw.(segWrap).origSeg
segs = append(segs, seg)
}
return segs
}

type segWrap struct {
intfs []pathpol.PathInterface
key string
origSeg *seg.PathSegment
}

func wrap(seg *seg.PathSegment, dir Direction) segWrap {
intfs := make([]pathpol.PathInterface, 0, len(seg.ASEntries))
keyParts := make([]string, 0, len(seg.ASEntries))
for _, asEntry := range seg.ASEntries {
for _, hopEntry := range asEntry.HopEntries {
hopField, err := hopEntry.HopField()
// This library expects the segments to be verified first.
if err != nil {
panic(err)
}
for _, ifid := range []common.IFIDType{hopField.ConsIngress, hopField.ConsEgress} {
if ifid != 0 {
intfs = append(intfs, pathInterface{ia: asEntry.IA(), ifid: ifid})
keyParts = append(keyParts, fmt.Sprintf("%s#%d", asEntry.IA(), ifid))
}
}
}
}
if dir == ReverseConsDir {
// reverse interfaces
for left, right := 0, len(intfs)-1; left < right; left, right = left+1, right-1 {
intfs[left], intfs[right] = intfs[right], intfs[left]
}
}
return segWrap{
intfs: intfs,
key: strings.Join(keyParts, " "),
origSeg: seg,
}
}

func (s segWrap) Interfaces() []pathpol.PathInterface { return s.intfs }
func (s segWrap) Key() string { return s.key }

type pathInterface struct {
ia addr.IA
ifid common.IFIDType
}

func (i pathInterface) IA() addr.IA { return i.ia }
func (i pathInterface) IfId() common.IFIDType { return i.ifid }
92 changes: 92 additions & 0 deletions go/path_srv/internal/segutil/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// 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 segutil

import (
"fmt"
"strings"
"testing"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"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/pathpol"
"github.com/scionproto/scion/go/lib/xtest"
"github.com/scionproto/scion/go/lib/xtest/graph"
)

func TestFilter(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
g := graph.NewDefaultGraph(ctrl)
ia110 := xtest.MustParseIA("1-ff00:0:110")
ia111 := xtest.MustParseIA("1-ff00:0:111")
ia120 := xtest.MustParseIA("1-ff00:0:120")
seg110To120 := g.Beacon([]common.IFIDType{graph.If_110_X_120_A})
seg110To130 := g.Beacon([]common.IFIDType{graph.If_110_X_130_A})

tests := map[string]struct {
Segs seg.Segments
Dir Direction
Policy *pathpol.Policy
ExpectedSegs seg.Segments
}{
"no policy": {
Segs: seg.Segments{seg110To120, seg110To130},
ExpectedSegs: seg.Segments{seg110To120, seg110To130},
},
"acl policy": {
Segs: seg.Segments{seg110To120, seg110To130},
Policy: &pathpol.Policy{ACL: acl(t, ia120)},
ExpectedSegs: seg.Segments{seg110To130},
},
"sequence policy doesn't filter": {
Segs: seg.Segments{seg110To120, seg110To130},
Policy: &pathpol.Policy{Sequence: sequence(t, ia111, ia110)},
ExpectedSegs: seg.Segments{seg110To120, seg110To130},
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
assert.ElementsMatch(t, test.ExpectedSegs, Filter(test.Segs, test.Policy, test.Dir))
})
}
}

func acl(t testing.TB, disallow addr.IA) *pathpol.ACL {
var disallowEntry pathpol.ACLEntry
err := disallowEntry.LoadFromString(fmt.Sprintf("- %s", disallow))
xtest.FailOnErr(t, err)
var allowEntry pathpol.ACLEntry
err = allowEntry.LoadFromString("+")
xtest.FailOnErr(t, err)
acl, err := pathpol.NewACL(&disallowEntry, &allowEntry)
xtest.FailOnErr(t, err)
return acl
}

func sequence(t testing.TB, ias ...addr.IA) *pathpol.Sequence {
parts := make([]string, 0, len(ias))
for _, ia := range ias {
parts = append(parts, ia.String())
}
seq, err := pathpol.NewSequence(strings.Join(parts, " "))
require.NoError(t, err)
return seq
}
1 change: 0 additions & 1 deletion go/sciond/internal/fetcher/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,4 @@ func newPathWrap(p *combinator.Path) pathWrap {
}

func (p pathWrap) Interfaces() []pathpol.PathInterface { return p.intfs }
func (p pathWrap) IsPartial() bool { return false }
func (p pathWrap) Key() string { return p.key }

0 comments on commit 2bbb531

Please sign in to comment.