summaryrefslogtreecommitdiffhomepage
path: root/table
diff options
context:
space:
mode:
authorurban <bodozoglou@gmail.com>2018-02-20 21:48:23 +0000
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-06-05 19:57:21 +0900
commit5d15b3f290a4305e0c8b7d651834c997d2b09910 (patch)
treebce6c7e82f8a2c9f1d400ffeabbb3f791a989e68 /table
parent5c506b6db14dac32e60bb2f880faf6317ede46e6 (diff)
table: support nexthop match policy
Diffstat (limited to 'table')
-rw-r--r--table/policy.go182
-rw-r--r--table/policy_test.go64
2 files changed, 246 insertions, 0 deletions
diff --git a/table/policy.go b/table/policy.go
index 2a99f93d..770eb81b 100644
--- a/table/policy.go
+++ b/table/policy.go
@@ -37,6 +37,7 @@ import (
type PolicyOptions struct {
Info *PeerInfo
ValidationResult *Validation
+ OldNextHop net.IP
}
type DefinedType int
@@ -49,6 +50,7 @@ const (
DEFINED_TYPE_COMMUNITY
DEFINED_TYPE_EXT_COMMUNITY
DEFINED_TYPE_LARGE_COMMUNITY
+ DEFINED_TYPE_NEXT_HOP
)
type RouteType int
@@ -154,6 +156,7 @@ const (
CONDITION_RPKI
CONDITION_ROUTE_TYPE
CONDITION_LARGE_COMMUNITY
+ CONDITION_NEXT_HOP
)
type ActionType int
@@ -535,6 +538,109 @@ func NewPrefixSet(c config.PrefixSet) (*PrefixSet, error) {
}, nil
}
+type NextHopSet struct {
+ list []net.IPNet
+}
+
+func (s *NextHopSet) Name() string {
+ return "NextHopSet: NO NAME"
+}
+
+func (s *NextHopSet) Type() DefinedType {
+ return DEFINED_TYPE_NEXT_HOP
+}
+
+func (lhs *NextHopSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = append(lhs.list, rhs.list...)
+ return nil
+}
+
+func (lhs *NextHopSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ ps := make([]net.IPNet, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range rhs.list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ ps = append(ps, x)
+ }
+ }
+ lhs.list = ps
+ return nil
+}
+
+func (lhs *NextHopSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*NextHopSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ return nil
+}
+
+func (s *NextHopSet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, n := range s.list {
+ list = append(list, n.String())
+ }
+ return list
+}
+
+func (s *NextHopSet) ToConfig() []string {
+ return s.List()
+}
+
+func (s *NextHopSet) String() string {
+ return "[ " + strings.Join(s.List(), ", ") + " ]"
+}
+
+func (s *NextHopSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewNextHopSetFromApiStruct(name string, list []net.IPNet) (*NextHopSet, error) {
+ return &NextHopSet{
+ list: list,
+ }, nil
+}
+
+func NewNextHopSet(c []string) (*NextHopSet, error) {
+ list := make([]net.IPNet, 0, len(c))
+ for _, x := range c {
+ _, cidr, err := net.ParseCIDR(x)
+ if err != nil {
+ addr := net.ParseIP(x)
+ if addr == nil {
+ return nil, fmt.Errorf("invalid address or prefix: %s", x)
+ }
+ mask := net.CIDRMask(32, 32)
+ if addr.To4() == nil {
+ mask = net.CIDRMask(128, 128)
+ }
+ cidr = &net.IPNet{
+ IP: addr,
+ Mask: mask,
+ }
+ }
+ list = append(list, *cidr)
+ }
+ return &NextHopSet{
+ list: list,
+ }, nil
+}
+
type NeighborSet struct {
name string
list []net.IPNet
@@ -1240,6 +1346,74 @@ type Condition interface {
Set() DefinedSet
}
+type NextHopCondition struct {
+ set *NextHopSet
+}
+
+func (c *NextHopCondition) Type() ConditionType {
+ return CONDITION_NEXT_HOP
+}
+
+func (c *NextHopCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *NextHopCondition) Name() string { return "" }
+
+func (c *NextHopCondition) String() string {
+ return c.set.String()
+}
+
+// compare next-hop ipaddress of this condition and source address of path
+// and, subsequent comparisons are skipped if that matches the conditions.
+// If NextHopSet's length is zero, return true.
+func (c *NextHopCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if len(c.set.list) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Debug("NextHop doesn't have elements")
+ return true
+ }
+
+ nexthop := path.GetNexthop()
+
+ // In cases where we advertise routes from iBGP to eBGP, we want to filter
+ // on the "original" nexthop. The current paths' nexthop has already been
+ // set and is ready to be advertised as per:
+ // https://tools.ietf.org/html/rfc4271#section-5.1.3
+ if options != nil && options.OldNextHop != nil &&
+ !options.OldNextHop.IsUnspecified() && !options.OldNextHop.Equal(nexthop) {
+ nexthop = options.OldNextHop
+ }
+
+ if nexthop == nil {
+ return false
+ }
+
+ for _, n := range c.set.list {
+ if n.Contains(nexthop) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func NewNextHopCondition(c []string) (*NextHopCondition, error) {
+ if len(c) == 0 {
+ return nil, nil
+ }
+
+ list, err := NewNextHopSet(c)
+ if err != nil {
+ return nil, nil
+ }
+
+ return &NextHopCondition{
+ set: list,
+ }, nil
+}
+
type PrefixCondition struct {
set *PrefixSet
option MatchOption
@@ -2469,6 +2643,9 @@ func (s *Statement) ToConfig() *config.Statement {
case *LargeCommunityCondition:
v := c.(*LargeCommunityCondition)
cond.BgpConditions.MatchLargeCommunitySet = config.MatchLargeCommunitySet{LargeCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *NextHopCondition:
+ v := c.(*NextHopCondition)
+ cond.BgpConditions.NextHopInList = v.set.List()
case *RpkiValidationCondition:
v := c.(*RpkiValidationCondition)
cond.BgpConditions.RpkiValidationResult = v.result
@@ -2673,6 +2850,9 @@ func NewStatement(c config.Statement) (*Statement, error) {
func() (Condition, error) {
return NewLargeCommunityCondition(c.Conditions.BgpConditions.MatchLargeCommunitySet)
},
+ func() (Condition, error) {
+ return NewNextHopCondition(c.Conditions.BgpConditions.NextHopInList)
+ },
}
cs = make([]Condition, 0, len(cfs))
for _, f := range cfs {
@@ -3046,6 +3226,7 @@ func (r *RoutingPolicy) validateCondition(v Condition) (err error) {
c := v.(*LargeCommunityCondition)
c.set = i.(*LargeCommunitySet)
}
+ case CONDITION_NEXT_HOP:
case CONDITION_AS_PATH_LENGTH:
case CONDITION_RPKI:
}
@@ -3155,6 +3336,7 @@ func (r *RoutingPolicy) reload(c config.RoutingPolicy) error {
}
dmap[DEFINED_TYPE_LARGE_COMMUNITY][y.Name()] = y
}
+
pmap := make(map[string]*Policy)
smap := make(map[string]*Statement)
for _, x := range c.PolicyDefinitions {
diff --git a/table/policy_test.go b/table/policy_test.go
index cf02b095..9e990dc9 100644
--- a/table/policy_test.go
+++ b/table/policy_test.go
@@ -501,6 +501,70 @@ func TestAsPathLengthConditionEvaluate(t *testing.T) {
assert.Equal(t, false, c.Evaluate(path, nil))
}
+func TestPolicyMatchAndAcceptNextHop(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Conditions.BgpConditions.NextHopInList = []string{"10.0.0.1/32"}
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_ACCEPT, pType)
+ assert.Equal(t, newPath, path)
+}
+
+func TestPolicyMatchAndRejectNextHop(t *testing.T) {
+ // create path
+ peer := &PeerInfo{AS: 65001, Address: net.ParseIP("10.0.0.1")}
+ origin := bgp.NewPathAttributeOrigin(0)
+ aspathParam := []bgp.AsPathParamInterface{bgp.NewAsPathParam(2, []uint16{65001})}
+ aspath := bgp.NewPathAttributeAsPath(aspathParam)
+ nexthop := bgp.NewPathAttributeNextHop("10.0.0.1")
+ med := bgp.NewPathAttributeMultiExitDisc(0)
+ pathAttributes := []bgp.PathAttributeInterface{origin, aspath, nexthop, med}
+ nlri := []*bgp.IPAddrPrefix{bgp.NewIPAddrPrefix(24, "10.10.0.101")}
+ updateMsg := bgp.NewBGPUpdateMessage(nil, pathAttributes, nlri)
+ path := ProcessMessage(updateMsg, peer, time.Now())[0]
+
+ // create policy
+ ps := createPrefixSet("ps1", "10.10.0.0/16", "21..24")
+ ns := createNeighborSet("ns1", "10.0.0.1")
+ ds := config.DefinedSets{}
+ ds.PrefixSets = []config.PrefixSet{ps}
+ ds.NeighborSets = []config.NeighborSet{ns}
+ s := createStatement("statement1", "ps1", "ns1", true)
+ s.Conditions.BgpConditions.NextHopInList = []string{"10.0.0.12"}
+ pd := createPolicyDefinition("pd1", s)
+ pl := createRoutingPolicy(ds, pd)
+
+ r := NewRoutingPolicy()
+ err := r.reload(pl)
+ assert.Nil(t, err)
+ pType, newPath := r.policyMap["pd1"].Apply(path, nil)
+ assert.Equal(t, ROUTE_TYPE_NONE, pType)
+ assert.Equal(t, newPath, path)
+}
+
func TestAsPathLengthConditionWithOtherCondition(t *testing.T) {
// setup
// create path