diff options
Diffstat (limited to 'table/policy.go')
-rw-r--r-- | table/policy.go | 182 |
1 files changed, 182 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 { |