summaryrefslogtreecommitdiffhomepage
path: root/internal/pkg/table/policy.go
diff options
context:
space:
mode:
authorFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 13:48:38 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2018-07-07 20:44:25 +0900
commitc4775c42510d1f1ddd55036dc19e982712fa6a0b (patch)
tree6ec8b61d4338c809e239e3003a2d32d480898e22 /internal/pkg/table/policy.go
parentb3079759aa13172fcb548a83da9a9653d8d5fed4 (diff)
follow Standard Go Project Layout
https://github.com/golang-standards/project-layout Now you can see clearly what are private and public library code. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Diffstat (limited to 'internal/pkg/table/policy.go')
-rw-r--r--internal/pkg/table/policy.go3994
1 files changed, 3994 insertions, 0 deletions
diff --git a/internal/pkg/table/policy.go b/internal/pkg/table/policy.go
new file mode 100644
index 00000000..23c2110c
--- /dev/null
+++ b/internal/pkg/table/policy.go
@@ -0,0 +1,3994 @@
+// Copyright (C) 2014-2016 Nippon Telegraph and Telephone Corporation.
+//
+// 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 table
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net"
+ "reflect"
+ "regexp"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+
+ "github.com/osrg/gobgp/internal/pkg/config"
+ "github.com/osrg/gobgp/pkg/packet/bgp"
+
+ radix "github.com/armon/go-radix"
+ log "github.com/sirupsen/logrus"
+)
+
+type PolicyOptions struct {
+ Info *PeerInfo
+ ValidationResult *Validation
+ OldNextHop net.IP
+}
+
+type DefinedType int
+
+const (
+ DEFINED_TYPE_PREFIX DefinedType = iota
+ DEFINED_TYPE_NEIGHBOR
+ DEFINED_TYPE_TAG
+ DEFINED_TYPE_AS_PATH
+ DEFINED_TYPE_COMMUNITY
+ DEFINED_TYPE_EXT_COMMUNITY
+ DEFINED_TYPE_LARGE_COMMUNITY
+ DEFINED_TYPE_NEXT_HOP
+)
+
+type RouteType int
+
+const (
+ ROUTE_TYPE_NONE RouteType = iota
+ ROUTE_TYPE_ACCEPT
+ ROUTE_TYPE_REJECT
+)
+
+func (t RouteType) String() string {
+ switch t {
+ case ROUTE_TYPE_NONE:
+ return "continue"
+ case ROUTE_TYPE_ACCEPT:
+ return "accept"
+ case ROUTE_TYPE_REJECT:
+ return "reject"
+ }
+ return fmt.Sprintf("unknown(%d)", t)
+}
+
+type PolicyDirection int
+
+const (
+ POLICY_DIRECTION_NONE PolicyDirection = iota
+ POLICY_DIRECTION_IN
+ POLICY_DIRECTION_IMPORT
+ POLICY_DIRECTION_EXPORT
+)
+
+func (d PolicyDirection) String() string {
+ switch d {
+ case POLICY_DIRECTION_IN:
+ return "in"
+ case POLICY_DIRECTION_IMPORT:
+ return "import"
+ case POLICY_DIRECTION_EXPORT:
+ return "export"
+ }
+ return fmt.Sprintf("unknown(%d)", d)
+}
+
+type MatchOption int
+
+const (
+ MATCH_OPTION_ANY MatchOption = iota
+ MATCH_OPTION_ALL
+ MATCH_OPTION_INVERT
+)
+
+func (o MatchOption) String() string {
+ switch o {
+ case MATCH_OPTION_ANY:
+ return "any"
+ case MATCH_OPTION_ALL:
+ return "all"
+ case MATCH_OPTION_INVERT:
+ return "invert"
+ default:
+ return fmt.Sprintf("MatchOption(%d)", o)
+ }
+}
+
+func (o MatchOption) ConvertToMatchSetOptionsRestrictedType() config.MatchSetOptionsRestrictedType {
+ switch o {
+ case MATCH_OPTION_ANY:
+ return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
+ case MATCH_OPTION_INVERT:
+ return config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT
+ }
+ return "unknown"
+}
+
+type MedActionType int
+
+const (
+ MED_ACTION_MOD MedActionType = iota
+ MED_ACTION_REPLACE
+)
+
+var CommunityOptionNameMap = map[config.BgpSetCommunityOptionType]string{
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD: "add",
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE: "remove",
+ config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE: "replace",
+}
+
+var CommunityOptionValueMap = map[string]config.BgpSetCommunityOptionType{
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD]: config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD,
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE,
+ CommunityOptionNameMap[config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE]: config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE,
+}
+
+type ConditionType int
+
+const (
+ CONDITION_PREFIX ConditionType = iota
+ CONDITION_NEIGHBOR
+ CONDITION_AS_PATH
+ CONDITION_COMMUNITY
+ CONDITION_EXT_COMMUNITY
+ CONDITION_AS_PATH_LENGTH
+ CONDITION_RPKI
+ CONDITION_ROUTE_TYPE
+ CONDITION_LARGE_COMMUNITY
+ CONDITION_NEXT_HOP
+ CONDITION_AFI_SAFI_IN
+)
+
+type ActionType int
+
+const (
+ ACTION_ROUTING ActionType = iota
+ ACTION_COMMUNITY
+ ACTION_EXT_COMMUNITY
+ ACTION_MED
+ ACTION_AS_PATH_PREPEND
+ ACTION_NEXTHOP
+ ACTION_LOCAL_PREF
+ ACTION_LARGE_COMMUNITY
+)
+
+func NewMatchOption(c interface{}) (MatchOption, error) {
+ switch t := c.(type) {
+ case config.MatchSetOptionsType:
+ t = t.DefaultAsNeeded()
+ switch t {
+ case config.MATCH_SET_OPTIONS_TYPE_ANY:
+ return MATCH_OPTION_ANY, nil
+ case config.MATCH_SET_OPTIONS_TYPE_ALL:
+ return MATCH_OPTION_ALL, nil
+ case config.MATCH_SET_OPTIONS_TYPE_INVERT:
+ return MATCH_OPTION_INVERT, nil
+ }
+ case config.MatchSetOptionsRestrictedType:
+ t = t.DefaultAsNeeded()
+ switch t {
+ case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY:
+ return MATCH_OPTION_ANY, nil
+ case config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT:
+ return MATCH_OPTION_INVERT, nil
+ }
+ }
+ return MATCH_OPTION_ANY, fmt.Errorf("invalid argument to create match option: %v", c)
+}
+
+type AttributeComparison int
+
+const (
+ // "== comparison"
+ ATTRIBUTE_EQ AttributeComparison = iota
+ // ">= comparison"
+ ATTRIBUTE_GE
+ // "<= comparison"
+ ATTRIBUTE_LE
+)
+
+func (c AttributeComparison) String() string {
+ switch c {
+ case ATTRIBUTE_EQ:
+ return "="
+ case ATTRIBUTE_GE:
+ return ">="
+ case ATTRIBUTE_LE:
+ return "<="
+ }
+ return "?"
+}
+
+const (
+ ASPATH_REGEXP_MAGIC = "(^|[,{}() ]|$)"
+)
+
+type DefinedSet interface {
+ Type() DefinedType
+ Name() string
+ Append(DefinedSet) error
+ Remove(DefinedSet) error
+ Replace(DefinedSet) error
+ String() string
+ List() []string
+}
+
+type DefinedSetMap map[DefinedType]map[string]DefinedSet
+
+type DefinedSetList []DefinedSet
+
+func (l DefinedSetList) Len() int {
+ return len(l)
+}
+
+func (l DefinedSetList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+func (l DefinedSetList) Less(i, j int) bool {
+ if l[i].Type() != l[j].Type() {
+ return l[i].Type() < l[j].Type()
+ }
+ return l[i].Name() < l[j].Name()
+}
+
+type Prefix struct {
+ Prefix *net.IPNet
+ AddressFamily bgp.RouteFamily
+ MasklengthRangeMax uint8
+ MasklengthRangeMin uint8
+}
+
+func (p *Prefix) Match(path *Path) bool {
+ rf := path.GetRouteFamily()
+ if rf != p.AddressFamily {
+ return false
+ }
+
+ var pAddr net.IP
+ var pMasklen uint8
+ switch rf {
+ case bgp.RF_IPv4_UC:
+ pAddr = path.GetNlri().(*bgp.IPAddrPrefix).Prefix
+ pMasklen = path.GetNlri().(*bgp.IPAddrPrefix).Length
+ case bgp.RF_IPv6_UC:
+ pAddr = path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix
+ pMasklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length
+ default:
+ return false
+ }
+
+ return (p.MasklengthRangeMin <= pMasklen && pMasklen <= p.MasklengthRangeMax) && p.Prefix.Contains(pAddr)
+}
+
+func (lhs *Prefix) Equal(rhs *Prefix) bool {
+ if lhs == rhs {
+ return true
+ }
+ if rhs == nil {
+ return false
+ }
+ return lhs.Prefix.String() == rhs.Prefix.String() && lhs.MasklengthRangeMin == rhs.MasklengthRangeMin && lhs.MasklengthRangeMax == rhs.MasklengthRangeMax
+}
+
+func (p *Prefix) PrefixString() string {
+ isZeros := func(p net.IP) bool {
+ for i := 0; i < len(p); i++ {
+ if p[i] != 0 {
+ return false
+ }
+ }
+ return true
+ }
+
+ ip := p.Prefix.IP
+ if p.AddressFamily == bgp.RF_IPv6_UC && isZeros(ip[0:10]) && ip[10] == 0xff && ip[11] == 0xff {
+ m, _ := p.Prefix.Mask.Size()
+ return fmt.Sprintf("::FFFF:%s/%d", ip.To16(), m)
+ }
+ return p.Prefix.String()
+}
+
+var _regexpPrefixRange = regexp.MustCompile(`(\d+)\.\.(\d+)`)
+
+func NewPrefix(c config.Prefix) (*Prefix, error) {
+ _, prefix, err := net.ParseCIDR(c.IpPrefix)
+ if err != nil {
+ return nil, err
+ }
+
+ rf := bgp.RF_IPv4_UC
+ if strings.Contains(c.IpPrefix, ":") {
+ rf = bgp.RF_IPv6_UC
+ }
+ p := &Prefix{
+ Prefix: prefix,
+ AddressFamily: rf,
+ }
+ maskRange := c.MasklengthRange
+
+ if maskRange == "" {
+ l, _ := prefix.Mask.Size()
+ maskLength := uint8(l)
+ p.MasklengthRangeMax = maskLength
+ p.MasklengthRangeMin = maskLength
+ return p, nil
+ }
+
+ elems := _regexpPrefixRange.FindStringSubmatch(maskRange)
+ if len(elems) != 3 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Prefix",
+ "MaskRangeFormat": maskRange,
+ }).Warn("mask length range format is invalid.")
+ return nil, fmt.Errorf("mask length range format is invalid")
+ }
+
+ // we've already checked the range is sane by regexp
+ min, _ := strconv.ParseUint(elems[1], 10, 8)
+ max, _ := strconv.ParseUint(elems[2], 10, 8)
+ p.MasklengthRangeMin = uint8(min)
+ p.MasklengthRangeMax = uint8(max)
+ return p, nil
+}
+
+type PrefixSet struct {
+ name string
+ tree *radix.Tree
+ family bgp.RouteFamily
+}
+
+func (s *PrefixSet) Name() string {
+ return s.name
+}
+
+func (s *PrefixSet) Type() DefinedType {
+ return DEFINED_TYPE_PREFIX
+}
+
+func (lhs *PrefixSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ // if either is empty, family can be ignored.
+ if lhs.tree.Len() != 0 && rhs.tree.Len() != 0 {
+ _, w, _ := lhs.tree.Minimum()
+ l := w.([]*Prefix)
+ _, v, _ := rhs.tree.Minimum()
+ r := v.([]*Prefix)
+ if l[0].AddressFamily != r[0].AddressFamily {
+ return fmt.Errorf("can't append different family")
+ }
+ }
+ rhs.tree.Walk(func(key string, v interface{}) bool {
+ w, ok := lhs.tree.Get(key)
+ if ok {
+ r := v.([]*Prefix)
+ l := w.([]*Prefix)
+ lhs.tree.Insert(key, append(l, r...))
+ } else {
+ lhs.tree.Insert(key, v)
+ }
+ return false
+ })
+ _, w, _ := lhs.tree.Minimum()
+ lhs.family = w.([]*Prefix)[0].AddressFamily
+ return nil
+}
+
+func (lhs *PrefixSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ rhs.tree.Walk(func(key string, v interface{}) bool {
+ w, ok := lhs.tree.Get(key)
+ if !ok {
+ return false
+ }
+ r := v.([]*Prefix)
+ l := w.([]*Prefix)
+ new := make([]*Prefix, 0, len(l))
+ for _, lp := range l {
+ delete := false
+ for _, rp := range r {
+ if lp.Equal(rp) {
+ delete = true
+ break
+ }
+ }
+ if !delete {
+ new = append(new, lp)
+ }
+ }
+ if len(new) == 0 {
+ lhs.tree.Delete(key)
+ } else {
+ lhs.tree.Insert(key, new)
+ }
+ return false
+ })
+ return nil
+}
+
+func (lhs *PrefixSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*PrefixSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.tree = rhs.tree
+ lhs.family = rhs.family
+ return nil
+}
+
+func (s *PrefixSet) List() []string {
+ var list []string
+ s.tree.Walk(func(s string, v interface{}) bool {
+ ps := v.([]*Prefix)
+ for _, p := range ps {
+ list = append(list, fmt.Sprintf("%s %d..%d", p.PrefixString(), p.MasklengthRangeMin, p.MasklengthRangeMax))
+ }
+ return false
+ })
+ return list
+}
+
+func (s *PrefixSet) ToConfig() *config.PrefixSet {
+ list := make([]config.Prefix, 0, s.tree.Len())
+ s.tree.Walk(func(s string, v interface{}) bool {
+ ps := v.([]*Prefix)
+ for _, p := range ps {
+ list = append(list, config.Prefix{IpPrefix: p.PrefixString(), MasklengthRange: fmt.Sprintf("%d..%d", p.MasklengthRangeMin, p.MasklengthRangeMax)})
+ }
+ return false
+ })
+ return &config.PrefixSet{
+ PrefixSetName: s.name,
+ PrefixList: list,
+ }
+}
+
+func (s *PrefixSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *PrefixSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewPrefixSetFromApiStruct(name string, prefixes []*Prefix) (*PrefixSet, error) {
+ if name == "" {
+ return nil, fmt.Errorf("empty prefix set name")
+ }
+ tree := radix.New()
+ var family bgp.RouteFamily
+ for i, x := range prefixes {
+ if i == 0 {
+ family = x.AddressFamily
+ } else if family != x.AddressFamily {
+ return nil, fmt.Errorf("multiple families")
+ }
+ key := CidrToRadixkey(x.Prefix.String())
+ d, ok := tree.Get(key)
+ if ok {
+ ps := d.([]*Prefix)
+ tree.Insert(key, append(ps, x))
+ } else {
+ tree.Insert(key, []*Prefix{x})
+ }
+ }
+ return &PrefixSet{
+ name: name,
+ tree: tree,
+ family: family,
+ }, nil
+}
+
+func NewPrefixSet(c config.PrefixSet) (*PrefixSet, error) {
+ name := c.PrefixSetName
+ if name == "" {
+ if len(c.PrefixList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty prefix set name")
+ }
+ tree := radix.New()
+ var family bgp.RouteFamily
+ for i, x := range c.PrefixList {
+ y, err := NewPrefix(x)
+ if err != nil {
+ return nil, err
+ }
+ if i == 0 {
+ family = y.AddressFamily
+ } else if family != y.AddressFamily {
+ return nil, fmt.Errorf("multiple families")
+ }
+ key := CidrToRadixkey(y.Prefix.String())
+ d, ok := tree.Get(key)
+ if ok {
+ ps := d.([]*Prefix)
+ tree.Insert(key, append(ps, y))
+ } else {
+ tree.Insert(key, []*Prefix{y})
+ }
+ }
+ return &PrefixSet{
+ name: name,
+ tree: tree,
+ family: family,
+ }, 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
+}
+
+func (s *NeighborSet) Name() string {
+ return s.name
+}
+
+func (s *NeighborSet) Type() DefinedType {
+ return DEFINED_TYPE_NEIGHBOR
+}
+
+func (lhs *NeighborSet) Append(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = append(lhs.list, rhs.list...)
+ return nil
+}
+
+func (lhs *NeighborSet) Remove(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ 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 *NeighborSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*NeighborSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ return nil
+}
+
+func (s *NeighborSet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, n := range s.list {
+ list = append(list, n.String())
+ }
+ return list
+}
+
+func (s *NeighborSet) ToConfig() *config.NeighborSet {
+ return &config.NeighborSet{
+ NeighborSetName: s.name,
+ NeighborInfoList: s.List(),
+ }
+}
+
+func (s *NeighborSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *NeighborSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewNeighborSetFromApiStruct(name string, list []net.IPNet) (*NeighborSet, error) {
+ return &NeighborSet{
+ name: name,
+ list: list,
+ }, nil
+}
+
+func NewNeighborSet(c config.NeighborSet) (*NeighborSet, error) {
+ name := c.NeighborSetName
+ if name == "" {
+ if len(c.NeighborInfoList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty neighbor set name")
+ }
+ list := make([]net.IPNet, 0, len(c.NeighborInfoList))
+ for _, x := range c.NeighborInfoList {
+ _, 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 &NeighborSet{
+ name: name,
+ list: list,
+ }, nil
+}
+
+type singleAsPathMatchMode int
+
+const (
+ INCLUDE singleAsPathMatchMode = iota
+ LEFT_MOST
+ ORIGIN
+ ONLY
+)
+
+type singleAsPathMatch struct {
+ asn uint32
+ mode singleAsPathMatchMode
+}
+
+func (lhs *singleAsPathMatch) Equal(rhs *singleAsPathMatch) bool {
+ return lhs.asn == rhs.asn && lhs.mode == rhs.mode
+}
+
+func (lhs *singleAsPathMatch) String() string {
+ switch lhs.mode {
+ case INCLUDE:
+ return fmt.Sprintf("_%d_", lhs.asn)
+ case LEFT_MOST:
+ return fmt.Sprintf("^%d_", lhs.asn)
+ case ORIGIN:
+ return fmt.Sprintf("_%d$", lhs.asn)
+ case ONLY:
+ return fmt.Sprintf("^%d$", lhs.asn)
+ }
+ return ""
+}
+
+func (m *singleAsPathMatch) Match(aspath []uint32) bool {
+ if len(aspath) == 0 {
+ return false
+ }
+ switch m.mode {
+ case INCLUDE:
+ for _, asn := range aspath {
+ if m.asn == asn {
+ return true
+ }
+ }
+ case LEFT_MOST:
+ if m.asn == aspath[0] {
+ return true
+ }
+ case ORIGIN:
+ if m.asn == aspath[len(aspath)-1] {
+ return true
+ }
+ case ONLY:
+ if len(aspath) == 1 && m.asn == aspath[0] {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ _regexpLeftMostRe = regexp.MustCompile(`$\^([0-9]+)_^`)
+ _regexpOriginRe = regexp.MustCompile(`^_([0-9]+)\$$`)
+ _regexpIncludeRe = regexp.MustCompile("^_([0-9]+)_$")
+ _regexpOnlyRe = regexp.MustCompile(`^\^([0-9]+)\$$`)
+)
+
+func NewSingleAsPathMatch(arg string) *singleAsPathMatch {
+ switch {
+ case _regexpLeftMostRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpLeftMostRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: LEFT_MOST,
+ }
+ case _regexpOriginRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpOriginRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: ORIGIN,
+ }
+ case _regexpIncludeRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpIncludeRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: INCLUDE,
+ }
+ case _regexpOnlyRe.MatchString(arg):
+ asn, _ := strconv.ParseUint(_regexpOnlyRe.FindStringSubmatch(arg)[1], 10, 32)
+ return &singleAsPathMatch{
+ asn: uint32(asn),
+ mode: ONLY,
+ }
+ }
+ return nil
+}
+
+type AsPathSet struct {
+ typ DefinedType
+ name string
+ list []*regexp.Regexp
+ singleList []*singleAsPathMatch
+}
+
+func (s *AsPathSet) Name() string {
+ return s.name
+}
+
+func (s *AsPathSet) Type() DefinedType {
+ return s.typ
+}
+
+func (lhs *AsPathSet) Append(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ lhs.list = append(lhs.list, arg.(*AsPathSet).list...)
+ lhs.singleList = append(lhs.singleList, arg.(*AsPathSet).singleList...)
+ return nil
+}
+
+func (lhs *AsPathSet) Remove(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ newList := make([]*regexp.Regexp, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range arg.(*AsPathSet).list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ newList = append(newList, x)
+ }
+ }
+ lhs.list = newList
+ newSingleList := make([]*singleAsPathMatch, 0, len(lhs.singleList))
+ for _, x := range lhs.singleList {
+ found := false
+ for _, y := range arg.(*AsPathSet).singleList {
+ if x.Equal(y) {
+ found = true
+ break
+ }
+ }
+ if !found {
+ newSingleList = append(newSingleList, x)
+ }
+ }
+ lhs.singleList = newSingleList
+ return nil
+}
+
+func (lhs *AsPathSet) Replace(arg DefinedSet) error {
+ rhs, ok := arg.(*AsPathSet)
+ if !ok {
+ return fmt.Errorf("type cast failed")
+ }
+ lhs.list = rhs.list
+ lhs.singleList = rhs.singleList
+ return nil
+}
+
+func (s *AsPathSet) List() []string {
+ list := make([]string, 0, len(s.list)+len(s.singleList))
+ for _, exp := range s.singleList {
+ list = append(list, exp.String())
+ }
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *AsPathSet) ToConfig() *config.AsPathSet {
+ return &config.AsPathSet{
+ AsPathSetName: s.name,
+ AsPathList: s.List(),
+ }
+}
+
+func (s *AsPathSet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *AsPathSet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewAsPathSet(c config.AsPathSet) (*AsPathSet, error) {
+ name := c.AsPathSetName
+ if name == "" {
+ if len(c.AsPathList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty as-path set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.AsPathList))
+ singleList := make([]*singleAsPathMatch, 0, len(c.AsPathList))
+ for _, x := range c.AsPathList {
+ if s := NewSingleAsPathMatch(x); s != nil {
+ singleList = append(singleList, s)
+ } else {
+ exp, err := regexp.Compile(strings.Replace(x, "_", ASPATH_REGEXP_MAGIC, -1))
+ if err != nil {
+ return nil, fmt.Errorf("invalid regular expression: %s", x)
+ }
+ list = append(list, exp)
+ }
+ }
+ return &AsPathSet{
+ typ: DEFINED_TYPE_AS_PATH,
+ name: name,
+ list: list,
+ singleList: singleList,
+ }, nil
+}
+
+type regExpSet struct {
+ typ DefinedType
+ name string
+ list []*regexp.Regexp
+}
+
+func (s *regExpSet) Name() string {
+ return s.name
+}
+
+func (s *regExpSet) Type() DefinedType {
+ return s.typ
+}
+
+func (lhs *regExpSet) Append(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ var list []*regexp.Regexp
+ switch lhs.Type() {
+ case DEFINED_TYPE_AS_PATH:
+ list = arg.(*AsPathSet).list
+ case DEFINED_TYPE_COMMUNITY:
+ list = arg.(*CommunitySet).list
+ case DEFINED_TYPE_EXT_COMMUNITY:
+ list = arg.(*ExtCommunitySet).list
+ case DEFINED_TYPE_LARGE_COMMUNITY:
+ list = arg.(*LargeCommunitySet).list
+ default:
+ return fmt.Errorf("invalid defined-set type: %d", lhs.Type())
+ }
+ lhs.list = append(lhs.list, list...)
+ return nil
+}
+
+func (lhs *regExpSet) Remove(arg DefinedSet) error {
+ if lhs.Type() != arg.Type() {
+ return fmt.Errorf("can't append to different type of defined-set")
+ }
+ var list []*regexp.Regexp
+ switch lhs.Type() {
+ case DEFINED_TYPE_AS_PATH:
+ list = arg.(*AsPathSet).list
+ case DEFINED_TYPE_COMMUNITY:
+ list = arg.(*CommunitySet).list
+ case DEFINED_TYPE_EXT_COMMUNITY:
+ list = arg.(*ExtCommunitySet).list
+ case DEFINED_TYPE_LARGE_COMMUNITY:
+ list = arg.(*LargeCommunitySet).list
+ default:
+ return fmt.Errorf("invalid defined-set type: %d", lhs.Type())
+ }
+ ps := make([]*regexp.Regexp, 0, len(lhs.list))
+ for _, x := range lhs.list {
+ found := false
+ for _, y := range list {
+ if x.String() == y.String() {
+ found = true
+ break
+ }
+ }
+ if !found {
+ ps = append(ps, x)
+ }
+ }
+ lhs.list = ps
+ return nil
+}
+
+func (lhs *regExpSet) Replace(arg DefinedSet) error {
+ switch c := arg.(type) {
+ case *CommunitySet:
+ lhs.list = c.list
+ case *ExtCommunitySet:
+ lhs.list = c.list
+ case *LargeCommunitySet:
+ lhs.list = c.list
+ default:
+ return fmt.Errorf("type cast failed")
+ }
+ return nil
+}
+
+type CommunitySet struct {
+ regExpSet
+}
+
+func (s *CommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *CommunitySet) ToConfig() *config.CommunitySet {
+ return &config.CommunitySet{
+ CommunitySetName: s.name,
+ CommunityList: s.List(),
+ }
+}
+
+func (s *CommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *CommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+var _regexpCommunity = regexp.MustCompile(`(\d+):(\d+)`)
+
+func ParseCommunity(arg string) (uint32, error) {
+ i, err := strconv.ParseUint(arg, 10, 32)
+ if err == nil {
+ return uint32(i), nil
+ }
+
+ elems := _regexpCommunity.FindStringSubmatch(arg)
+ if len(elems) == 3 {
+ fst, _ := strconv.ParseUint(elems[1], 10, 16)
+ snd, _ := strconv.ParseUint(elems[2], 10, 16)
+ return uint32(fst<<16 | snd), nil
+ }
+ for i, v := range bgp.WellKnownCommunityNameMap {
+ if arg == v {
+ return uint32(i), nil
+ }
+ }
+ return 0, fmt.Errorf("failed to parse %s as community", arg)
+}
+
+func ParseExtCommunity(arg string) (bgp.ExtendedCommunityInterface, error) {
+ var subtype bgp.ExtendedCommunityAttrSubType
+ var value string
+ elems := strings.SplitN(arg, ":", 2)
+
+ isValidationState := func(s string) bool {
+ s = strings.ToLower(s)
+ r := s == bgp.VALIDATION_STATE_VALID.String()
+ r = r || s == bgp.VALIDATION_STATE_NOT_FOUND.String()
+ return r || s == bgp.VALIDATION_STATE_INVALID.String()
+ }
+ if len(elems) < 2 && (len(elems) < 1 && !isValidationState(elems[0])) {
+ return nil, fmt.Errorf("invalid ext-community (rt|soo):<value> | valid | not-found | invalid")
+ }
+ if isValidationState(elems[0]) {
+ subtype = bgp.EC_SUBTYPE_ORIGIN_VALIDATION
+ value = elems[0]
+ } else {
+ switch strings.ToLower(elems[0]) {
+ case "rt":
+ subtype = bgp.EC_SUBTYPE_ROUTE_TARGET
+ case "soo":
+ subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN
+ default:
+ return nil, fmt.Errorf("invalid ext-community (rt|soo):<value> | valid | not-found | invalid")
+ }
+ value = elems[1]
+ }
+ return bgp.ParseExtendedCommunity(subtype, value)
+}
+
+var _regexpCommunity2 = regexp.MustCompile(`(\d+.)*\d+:\d+`)
+
+func ParseCommunityRegexp(arg string) (*regexp.Regexp, error) {
+ i, err := strconv.ParseUint(arg, 10, 32)
+ if err == nil {
+ return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
+ }
+
+ if _regexpCommunity2.MatchString(arg) {
+ return regexp.Compile(fmt.Sprintf("^%s$", arg))
+ }
+
+ for i, v := range bgp.WellKnownCommunityNameMap {
+ if strings.Replace(strings.ToLower(arg), "_", "-", -1) == v {
+ return regexp.Compile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff))
+ }
+ }
+
+ return regexp.Compile(arg)
+}
+
+func ParseExtCommunityRegexp(arg string) (bgp.ExtendedCommunityAttrSubType, *regexp.Regexp, error) {
+ var subtype bgp.ExtendedCommunityAttrSubType
+ elems := strings.SplitN(arg, ":", 2)
+ if len(elems) < 2 {
+ return subtype, nil, fmt.Errorf("invalid ext-community format([rt|soo]:<value>)")
+ }
+ switch strings.ToLower(elems[0]) {
+ case "rt":
+ subtype = bgp.EC_SUBTYPE_ROUTE_TARGET
+ case "soo":
+ subtype = bgp.EC_SUBTYPE_ROUTE_ORIGIN
+ default:
+ return subtype, nil, fmt.Errorf("unknown ext-community subtype. rt, soo is supported")
+ }
+ exp, err := ParseCommunityRegexp(elems[1])
+ return subtype, exp, err
+}
+
+func NewCommunitySet(c config.CommunitySet) (*CommunitySet, error) {
+ name := c.CommunitySetName
+ if name == "" {
+ if len(c.CommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.CommunityList))
+ for _, x := range c.CommunityList {
+ exp, err := ParseCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ }
+ return &CommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ }, nil
+}
+
+type ExtCommunitySet struct {
+ regExpSet
+ subtypeList []bgp.ExtendedCommunityAttrSubType
+}
+
+func (s *ExtCommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ f := func(idx int, arg string) string {
+ switch s.subtypeList[idx] {
+ case bgp.EC_SUBTYPE_ROUTE_TARGET:
+ return fmt.Sprintf("rt:%s", arg)
+ case bgp.EC_SUBTYPE_ROUTE_ORIGIN:
+ return fmt.Sprintf("soo:%s", arg)
+ case bgp.EC_SUBTYPE_ORIGIN_VALIDATION:
+ return arg
+ default:
+ return fmt.Sprintf("%d:%s", s.subtypeList[idx], arg)
+ }
+ }
+ for idx, exp := range s.list {
+ list = append(list, f(idx, exp.String()))
+ }
+ return list
+}
+
+func (s *ExtCommunitySet) ToConfig() *config.ExtCommunitySet {
+ return &config.ExtCommunitySet{
+ ExtCommunitySetName: s.name,
+ ExtCommunityList: s.List(),
+ }
+}
+
+func (s *ExtCommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *ExtCommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+func NewExtCommunitySet(c config.ExtCommunitySet) (*ExtCommunitySet, error) {
+ name := c.ExtCommunitySetName
+ if name == "" {
+ if len(c.ExtCommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty ext-community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.ExtCommunityList))
+ subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.ExtCommunityList))
+ for _, x := range c.ExtCommunityList {
+ subtype, exp, err := ParseExtCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ subtypeList = append(subtypeList, subtype)
+ }
+ return &ExtCommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_EXT_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ subtypeList: subtypeList,
+ }, nil
+}
+
+func (s *ExtCommunitySet) Append(arg DefinedSet) error {
+ err := s.regExpSet.Append(arg)
+ if err != nil {
+ return err
+ }
+ sList := arg.(*ExtCommunitySet).subtypeList
+ s.subtypeList = append(s.subtypeList, sList...)
+ return nil
+}
+
+type LargeCommunitySet struct {
+ regExpSet
+}
+
+func (s *LargeCommunitySet) List() []string {
+ list := make([]string, 0, len(s.list))
+ for _, exp := range s.list {
+ list = append(list, exp.String())
+ }
+ return list
+}
+
+func (s *LargeCommunitySet) ToConfig() *config.LargeCommunitySet {
+ return &config.LargeCommunitySet{
+ LargeCommunitySetName: s.name,
+ LargeCommunityList: s.List(),
+ }
+}
+
+func (s *LargeCommunitySet) String() string {
+ return strings.Join(s.List(), "\n")
+}
+
+func (s *LargeCommunitySet) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+var _regexpCommunityLarge = regexp.MustCompile(`\d+:\d+:\d+`)
+
+func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) {
+ if _regexpCommunityLarge.MatchString(arg) {
+ return regexp.Compile(fmt.Sprintf("^%s$", arg))
+ }
+ exp, err := regexp.Compile(arg)
+ if err != nil {
+ return nil, fmt.Errorf("invalid large-community format: %v", err)
+ }
+
+ return exp, nil
+}
+
+func NewLargeCommunitySet(c config.LargeCommunitySet) (*LargeCommunitySet, error) {
+ name := c.LargeCommunitySetName
+ if name == "" {
+ if len(c.LargeCommunityList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("empty large community set name")
+ }
+ list := make([]*regexp.Regexp, 0, len(c.LargeCommunityList))
+ for _, x := range c.LargeCommunityList {
+ exp, err := ParseLargeCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, exp)
+ }
+ return &LargeCommunitySet{
+ regExpSet: regExpSet{
+ typ: DEFINED_TYPE_LARGE_COMMUNITY,
+ name: name,
+ list: list,
+ },
+ }, nil
+}
+
+type Condition interface {
+ Name() string
+ Type() ConditionType
+ Evaluate(*Path, *PolicyOptions) bool
+ 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
+}
+
+func (c *PrefixCondition) Type() ConditionType {
+ return CONDITION_PREFIX
+}
+
+func (c *PrefixCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *PrefixCondition) Option() MatchOption {
+ return c.option
+}
+
+// compare prefixes in this condition and nlri of path and
+// subsequent comparison is skipped if that matches the conditions.
+// If PrefixList's length is zero, return true.
+func (c *PrefixCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ var key string
+ var masklen uint8
+ keyf := func(ip net.IP, ones int) string {
+ var buffer bytes.Buffer
+ for i := 0; i < len(ip) && i < ones; i++ {
+ buffer.WriteString(fmt.Sprintf("%08b", ip[i]))
+ }
+ return buffer.String()[:ones]
+ }
+ family := path.GetRouteFamily()
+ switch family {
+ case bgp.RF_IPv4_UC:
+ masklen = path.GetNlri().(*bgp.IPAddrPrefix).Length
+ key = keyf(path.GetNlri().(*bgp.IPAddrPrefix).Prefix, int(masklen))
+ case bgp.RF_IPv6_UC:
+ masklen = path.GetNlri().(*bgp.IPv6AddrPrefix).Length
+ key = keyf(path.GetNlri().(*bgp.IPv6AddrPrefix).Prefix, int(masklen))
+ default:
+ return false
+ }
+ if family != c.set.family {
+ return false
+ }
+
+ result := false
+ _, ps, ok := c.set.tree.LongestPrefix(key)
+ if ok {
+ for _, p := range ps.([]*Prefix) {
+ if p.MasklengthRangeMin <= masklen && masklen <= p.MasklengthRangeMax {
+ result = true
+ break
+ }
+ }
+ }
+
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+
+ return result
+}
+
+func (c *PrefixCondition) Name() string { return c.set.name }
+
+func NewPrefixCondition(c config.MatchPrefixSet) (*PrefixCondition, error) {
+ if c.PrefixSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &PrefixCondition{
+ set: &PrefixSet{
+ name: c.PrefixSet,
+ },
+ option: o,
+ }, nil
+}
+
+type NeighborCondition struct {
+ set *NeighborSet
+ option MatchOption
+}
+
+func (c *NeighborCondition) Type() ConditionType {
+ return CONDITION_NEIGHBOR
+}
+
+func (c *NeighborCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *NeighborCondition) Option() MatchOption {
+ return c.option
+}
+
+// compare neighbor ipaddress of this condition and source address of path
+// and, subsequent comparisons are skipped if that matches the conditions.
+// If NeighborList's length is zero, return true.
+func (c *NeighborCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if len(c.set.list) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Debug("NeighborList doesn't have elements")
+ return true
+ }
+
+ neighbor := path.GetSource().Address
+ if options != nil && options.Info != nil && options.Info.Address != nil {
+ neighbor = options.Info.Address
+ }
+
+ if neighbor == nil {
+ return false
+ }
+ result := false
+ for _, n := range c.set.list {
+ if n.Contains(neighbor) {
+ result = true
+ break
+ }
+ }
+
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+
+ return result
+}
+
+func (c *NeighborCondition) Name() string { return c.set.name }
+
+func NewNeighborCondition(c config.MatchNeighborSet) (*NeighborCondition, error) {
+ if c.NeighborSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &NeighborCondition{
+ set: &NeighborSet{
+ name: c.NeighborSet,
+ },
+ option: o,
+ }, nil
+}
+
+type AsPathCondition struct {
+ set *AsPathSet
+ option MatchOption
+}
+
+func (c *AsPathCondition) Type() ConditionType {
+ return CONDITION_AS_PATH
+}
+
+func (c *AsPathCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *AsPathCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *AsPathCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ if len(c.set.singleList) > 0 {
+ aspath := path.GetAsSeqList()
+ for _, m := range c.set.singleList {
+ result := m.Match(aspath)
+ if c.option == MATCH_OPTION_ALL && !result {
+ return false
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ return true
+ }
+ if c.option == MATCH_OPTION_INVERT && result {
+ return false
+ }
+ }
+ }
+ if len(c.set.list) > 0 {
+ aspath := path.GetAsString()
+ for _, r := range c.set.list {
+ result := r.MatchString(aspath)
+ if c.option == MATCH_OPTION_ALL && !result {
+ return false
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ return true
+ }
+ if c.option == MATCH_OPTION_INVERT && result {
+ return false
+ }
+ }
+ }
+ if c.option == MATCH_OPTION_ANY {
+ return false
+ }
+ return true
+}
+
+func (c *AsPathCondition) Name() string { return c.set.name }
+
+func NewAsPathCondition(c config.MatchAsPathSet) (*AsPathCondition, error) {
+ if c.AsPathSet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &AsPathCondition{
+ set: &AsPathSet{
+ name: c.AsPathSet,
+ },
+ option: o,
+ }, nil
+}
+
+type CommunityCondition struct {
+ set *CommunitySet
+ option MatchOption
+}
+
+func (c *CommunityCondition) Type() ConditionType {
+ return CONDITION_COMMUNITY
+}
+
+func (c *CommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *CommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *CommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ cs := path.GetCommunities()
+ result := false
+ for _, x := range c.set.list {
+ result = false
+ for _, y := range cs {
+ if x.MatchString(fmt.Sprintf("%d:%d", y>>16, y&0x0000ffff)) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *CommunityCondition) Name() string { return c.set.name }
+
+func NewCommunityCondition(c config.MatchCommunitySet) (*CommunityCondition, error) {
+ if c.CommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &CommunityCondition{
+ set: &CommunitySet{
+ regExpSet: regExpSet{
+ name: c.CommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type ExtCommunityCondition struct {
+ set *ExtCommunitySet
+ option MatchOption
+}
+
+func (c *ExtCommunityCondition) Type() ConditionType {
+ return CONDITION_EXT_COMMUNITY
+}
+
+func (c *ExtCommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *ExtCommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *ExtCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ es := path.GetExtCommunities()
+ result := false
+ for _, x := range es {
+ result = false
+ typ, subtype := x.GetTypes()
+ // match only with transitive community. see RFC7153
+ if typ >= 0x3f {
+ continue
+ }
+ for idx, y := range c.set.list {
+ if subtype == c.set.subtypeList[idx] && y.MatchString(x.String()) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if c.option == MATCH_OPTION_ANY && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *ExtCommunityCondition) Name() string { return c.set.name }
+
+func NewExtCommunityCondition(c config.MatchExtCommunitySet) (*ExtCommunityCondition, error) {
+ if c.ExtCommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &ExtCommunityCondition{
+ set: &ExtCommunitySet{
+ regExpSet: regExpSet{
+ name: c.ExtCommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type LargeCommunityCondition struct {
+ set *LargeCommunitySet
+ option MatchOption
+}
+
+func (c *LargeCommunityCondition) Type() ConditionType {
+ return CONDITION_LARGE_COMMUNITY
+}
+
+func (c *LargeCommunityCondition) Set() DefinedSet {
+ return c.set
+}
+
+func (c *LargeCommunityCondition) Option() MatchOption {
+ return c.option
+}
+
+func (c *LargeCommunityCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ result := false
+ cs := path.GetLargeCommunities()
+ for _, x := range c.set.list {
+ result = false
+ for _, y := range cs {
+ if x.MatchString(y.String()) {
+ result = true
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_ALL && !result {
+ break
+ }
+ if (c.option == MATCH_OPTION_ANY || c.option == MATCH_OPTION_INVERT) && result {
+ break
+ }
+ }
+ if c.option == MATCH_OPTION_INVERT {
+ result = !result
+ }
+ return result
+}
+
+func (c *LargeCommunityCondition) Name() string { return c.set.name }
+
+func NewLargeCommunityCondition(c config.MatchLargeCommunitySet) (*LargeCommunityCondition, error) {
+ if c.LargeCommunitySet == "" {
+ return nil, nil
+ }
+ o, err := NewMatchOption(c.MatchSetOptions)
+ if err != nil {
+ return nil, err
+ }
+ return &LargeCommunityCondition{
+ set: &LargeCommunitySet{
+ regExpSet: regExpSet{
+ name: c.LargeCommunitySet,
+ },
+ },
+ option: o,
+ }, nil
+}
+
+type AsPathLengthCondition struct {
+ length uint32
+ operator AttributeComparison
+}
+
+func (c *AsPathLengthCondition) Type() ConditionType {
+ return CONDITION_AS_PATH_LENGTH
+}
+
+// compare AS_PATH length in the message's AS_PATH attribute with
+// the one in condition.
+func (c *AsPathLengthCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+
+ length := uint32(path.GetAsPathLen())
+ result := false
+ switch c.operator {
+ case ATTRIBUTE_EQ:
+ result = c.length == length
+ case ATTRIBUTE_GE:
+ result = c.length <= length
+ case ATTRIBUTE_LE:
+ result = c.length >= length
+ }
+
+ return result
+}
+
+func (c *AsPathLengthCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *AsPathLengthCondition) Name() string { return "" }
+
+func (c *AsPathLengthCondition) String() string {
+ return fmt.Sprintf("%s%d", c.operator, c.length)
+}
+
+func NewAsPathLengthCondition(c config.AsPathLength) (*AsPathLengthCondition, error) {
+ if c.Value == 0 && c.Operator == "" {
+ return nil, nil
+ }
+ var op AttributeComparison
+ if i := c.Operator.ToInt(); i < 0 {
+ return nil, fmt.Errorf("invalid as path length operator: %s", c.Operator)
+ } else {
+ // take mod 3 because we have extended openconfig attribute-comparison
+ // for simple configuration. see config.AttributeComparison definition
+ op = AttributeComparison(i % 3)
+ }
+ return &AsPathLengthCondition{
+ length: c.Value,
+ operator: op,
+ }, nil
+}
+
+type RpkiValidationCondition struct {
+ result config.RpkiValidationResultType
+}
+
+func (c *RpkiValidationCondition) Type() ConditionType {
+ return CONDITION_RPKI
+}
+
+func (c *RpkiValidationCondition) Evaluate(path *Path, options *PolicyOptions) bool {
+ if options != nil && options.ValidationResult != nil {
+ return c.result == options.ValidationResult.Status
+ }
+ return false
+}
+
+func (c *RpkiValidationCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *RpkiValidationCondition) Name() string { return "" }
+
+func (c *RpkiValidationCondition) String() string {
+ return string(c.result)
+}
+
+func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidationCondition, error) {
+ if c == config.RpkiValidationResultType("") || c == config.RPKI_VALIDATION_RESULT_TYPE_NONE {
+ return nil, nil
+ }
+ return &RpkiValidationCondition{
+ result: c,
+ }, nil
+}
+
+type RouteTypeCondition struct {
+ typ config.RouteType
+}
+
+func (c *RouteTypeCondition) Type() ConditionType {
+ return CONDITION_ROUTE_TYPE
+}
+
+func (c *RouteTypeCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ switch c.typ {
+ case config.ROUTE_TYPE_LOCAL:
+ return path.IsLocal()
+ case config.ROUTE_TYPE_INTERNAL:
+ return !path.IsLocal() && path.IsIBGP()
+ case config.ROUTE_TYPE_EXTERNAL:
+ return !path.IsLocal() && !path.IsIBGP()
+ }
+ return false
+}
+
+func (c *RouteTypeCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *RouteTypeCondition) Name() string { return "" }
+
+func (c *RouteTypeCondition) String() string {
+ return string(c.typ)
+}
+
+func NewRouteTypeCondition(c config.RouteType) (*RouteTypeCondition, error) {
+ if string(c) == "" || c == config.ROUTE_TYPE_NONE {
+ return nil, nil
+ }
+ if err := c.Validate(); err != nil {
+ return nil, err
+ }
+ return &RouteTypeCondition{
+ typ: c,
+ }, nil
+}
+
+type AfiSafiInCondition struct {
+ routeFamilies []bgp.RouteFamily
+}
+
+func (c *AfiSafiInCondition) Type() ConditionType {
+ return CONDITION_AFI_SAFI_IN
+}
+
+func (c *AfiSafiInCondition) Evaluate(path *Path, _ *PolicyOptions) bool {
+ for _, rf := range c.routeFamilies {
+ if path.GetRouteFamily() == rf {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *AfiSafiInCondition) Set() DefinedSet {
+ return nil
+}
+
+func (c *AfiSafiInCondition) Name() string { return "" }
+
+func (c *AfiSafiInCondition) String() string {
+ tmp := make([]string, 0, len(c.routeFamilies))
+ for _, afiSafi := range c.routeFamilies {
+ tmp = append(tmp, afiSafi.String())
+ }
+ return strings.Join(tmp, " ")
+}
+
+func NewAfiSafiInCondition(afiSafInConfig []config.AfiSafiType) (*AfiSafiInCondition, error) {
+ if afiSafInConfig == nil {
+ return nil, nil
+ }
+
+ routeFamilies := make([]bgp.RouteFamily, 0, len(afiSafInConfig))
+ for _, afiSafiValue := range afiSafInConfig {
+ if err := afiSafiValue.Validate(); err != nil {
+ return nil, err
+ }
+ rf, err := bgp.GetRouteFamily(string(afiSafiValue))
+ if err != nil {
+ return nil, err
+ }
+ routeFamilies = append(routeFamilies, rf)
+ }
+ return &AfiSafiInCondition{
+ routeFamilies: routeFamilies,
+ }, nil
+}
+
+type Action interface {
+ Type() ActionType
+ Apply(*Path, *PolicyOptions) *Path
+ String() string
+}
+
+type RoutingAction struct {
+ AcceptRoute bool
+}
+
+func (a *RoutingAction) Type() ActionType {
+ return ACTION_ROUTING
+}
+
+func (a *RoutingAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ if a.AcceptRoute {
+ return path
+ }
+ return nil
+}
+
+func (a *RoutingAction) String() string {
+ action := "reject"
+ if a.AcceptRoute {
+ action = "accept"
+ }
+ return action
+}
+
+func NewRoutingAction(c config.RouteDisposition) (*RoutingAction, error) {
+ var accept bool
+ switch c {
+ case config.RouteDisposition(""), config.ROUTE_DISPOSITION_NONE:
+ return nil, nil
+ case config.ROUTE_DISPOSITION_ACCEPT_ROUTE:
+ accept = true
+ case config.ROUTE_DISPOSITION_REJECT_ROUTE:
+ accept = false
+ default:
+ return nil, fmt.Errorf("invalid route disposition")
+ }
+ return &RoutingAction{
+ AcceptRoute: accept,
+ }, nil
+}
+
+type CommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []uint32
+ removeList []*regexp.Regexp
+}
+
+func RegexpRemoveCommunities(path *Path, exps []*regexp.Regexp) {
+ comms := path.GetCommunities()
+ newComms := make([]uint32, 0, len(comms))
+ for _, comm := range comms {
+ c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff)
+ match := false
+ for _, exp := range exps {
+ if exp.MatchString(c) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetCommunities(newComms, true)
+}
+
+func RegexpRemoveExtCommunities(path *Path, exps []*regexp.Regexp, subtypes []bgp.ExtendedCommunityAttrSubType) {
+ comms := path.GetExtCommunities()
+ newComms := make([]bgp.ExtendedCommunityInterface, 0, len(comms))
+ for _, comm := range comms {
+ match := false
+ typ, subtype := comm.GetTypes()
+ // match only with transitive community. see RFC7153
+ if typ >= 0x3f {
+ continue
+ }
+ for idx, exp := range exps {
+ if subtype == subtypes[idx] && exp.MatchString(comm.String()) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetExtCommunities(newComms, true)
+}
+
+func RegexpRemoveLargeCommunities(path *Path, exps []*regexp.Regexp) {
+ comms := path.GetLargeCommunities()
+ newComms := make([]*bgp.LargeCommunity, 0, len(comms))
+ for _, comm := range comms {
+ c := comm.String()
+ match := false
+ for _, exp := range exps {
+ if exp.MatchString(c) {
+ match = true
+ break
+ }
+ }
+ if !match {
+ newComms = append(newComms, comm)
+ }
+ }
+ path.SetLargeCommunities(newComms, true)
+}
+
+func (a *CommunityAction) Type() ActionType {
+ return ACTION_COMMUNITY
+}
+
+func (a *CommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveCommunities(path, a.removeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *CommunityAction) ToConfig() *config.SetCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ for _, comm := range a.list {
+ c := fmt.Sprintf("%d:%d", comm>>16, comm&0x0000ffff)
+ cs = append(cs, c)
+ }
+ for _, exp := range a.removeList {
+ cs = append(cs, exp.String())
+ }
+ return &config.SetCommunity{
+ Options: string(a.action),
+ SetCommunityMethod: config.SetCommunityMethod{CommunitiesList: cs},
+ }
+}
+
+func (a *CommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+// TODO: this is not efficient use of regexp, probably slow
+var _regexpCommunityReplaceString = regexp.MustCompile(`[\^\$]`)
+
+func (a *CommunityAction) String() string {
+ list := a.ToConfig().SetCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func NewCommunityAction(c config.SetCommunity) (*CommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)]
+ if !ok {
+ if len(c.SetCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []uint32
+ var removeList []*regexp.Regexp
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]uint32, 0, len(c.SetCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ exp, err := ParseCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ } else {
+ comm, err := ParseCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ }
+ }
+ return &CommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ }, nil
+}
+
+type ExtCommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []bgp.ExtendedCommunityInterface
+ removeList []*regexp.Regexp
+ subtypeList []bgp.ExtendedCommunityAttrSubType
+}
+
+func (a *ExtCommunityAction) Type() ActionType {
+ return ACTION_EXT_COMMUNITY
+}
+
+func (a *ExtCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetExtCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveExtCommunities(path, a.removeList, a.subtypeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetExtCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *ExtCommunityAction) ToConfig() *config.SetExtCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ f := func(idx int, arg string) string {
+ switch a.subtypeList[idx] {
+ case bgp.EC_SUBTYPE_ROUTE_TARGET:
+ return fmt.Sprintf("rt:%s", arg)
+ case bgp.EC_SUBTYPE_ROUTE_ORIGIN:
+ return fmt.Sprintf("soo:%s", arg)
+ case bgp.EC_SUBTYPE_ORIGIN_VALIDATION:
+ return arg
+ default:
+ return fmt.Sprintf("%d:%s", a.subtypeList[idx], arg)
+ }
+ }
+ for idx, c := range a.list {
+ cs = append(cs, f(idx, c.String()))
+ }
+ for idx, exp := range a.removeList {
+ cs = append(cs, f(idx, exp.String()))
+ }
+ return &config.SetExtCommunity{
+ Options: string(a.action),
+ SetExtCommunityMethod: config.SetExtCommunityMethod{
+ CommunitiesList: cs,
+ },
+ }
+}
+
+func (a *ExtCommunityAction) String() string {
+ list := a.ToConfig().SetExtCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func (a *ExtCommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewExtCommunityAction(c config.SetExtCommunity) (*ExtCommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(c.Options)]
+ if !ok {
+ if len(c.SetExtCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []bgp.ExtendedCommunityInterface
+ var removeList []*regexp.Regexp
+ subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]bgp.ExtendedCommunityInterface, 0, len(c.SetExtCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetExtCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ subtype, exp, err := ParseExtCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ subtypeList = append(subtypeList, subtype)
+ } else {
+ comm, err := ParseExtCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ _, subtype := comm.GetTypes()
+ subtypeList = append(subtypeList, subtype)
+ }
+ }
+ return &ExtCommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ subtypeList: subtypeList,
+ }, nil
+}
+
+type LargeCommunityAction struct {
+ action config.BgpSetCommunityOptionType
+ list []*bgp.LargeCommunity
+ removeList []*regexp.Regexp
+}
+
+func (a *LargeCommunityAction) Type() ActionType {
+ return ACTION_LARGE_COMMUNITY
+}
+
+func (a *LargeCommunityAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ switch a.action {
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_ADD:
+ path.SetLargeCommunities(a.list, false)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE:
+ RegexpRemoveLargeCommunities(path, a.removeList)
+ case config.BGP_SET_COMMUNITY_OPTION_TYPE_REPLACE:
+ path.SetLargeCommunities(a.list, true)
+ }
+ return path
+}
+
+func (a *LargeCommunityAction) ToConfig() *config.SetLargeCommunity {
+ cs := make([]string, 0, len(a.list)+len(a.removeList))
+ for _, comm := range a.list {
+ cs = append(cs, comm.String())
+ }
+ for _, exp := range a.removeList {
+ cs = append(cs, exp.String())
+ }
+ return &config.SetLargeCommunity{
+ SetLargeCommunityMethod: config.SetLargeCommunityMethod{CommunitiesList: cs},
+ Options: config.BgpSetCommunityOptionType(a.action),
+ }
+}
+
+func (a *LargeCommunityAction) String() string {
+ list := a.ToConfig().SetLargeCommunityMethod.CommunitiesList
+ l := _regexpCommunityReplaceString.ReplaceAllString(strings.Join(list, ", "), "")
+ return fmt.Sprintf("%s[%s]", a.action, l)
+}
+
+func (a *LargeCommunityAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewLargeCommunityAction(c config.SetLargeCommunity) (*LargeCommunityAction, error) {
+ a, ok := CommunityOptionValueMap[strings.ToLower(string(c.Options))]
+ if !ok {
+ if len(c.SetLargeCommunityMethod.CommunitiesList) == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("invalid option name: %s", c.Options)
+ }
+ var list []*bgp.LargeCommunity
+ var removeList []*regexp.Regexp
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ removeList = make([]*regexp.Regexp, 0, len(c.SetLargeCommunityMethod.CommunitiesList))
+ } else {
+ list = make([]*bgp.LargeCommunity, 0, len(c.SetLargeCommunityMethod.CommunitiesList))
+ }
+ for _, x := range c.SetLargeCommunityMethod.CommunitiesList {
+ if a == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
+ exp, err := ParseLargeCommunityRegexp(x)
+ if err != nil {
+ return nil, err
+ }
+ removeList = append(removeList, exp)
+ } else {
+ comm, err := bgp.ParseLargeCommunity(x)
+ if err != nil {
+ return nil, err
+ }
+ list = append(list, comm)
+ }
+ }
+ return &LargeCommunityAction{
+ action: a,
+ list: list,
+ removeList: removeList,
+ }, nil
+
+}
+
+type MedAction struct {
+ value int64
+ action MedActionType
+}
+
+func (a *MedAction) Type() ActionType {
+ return ACTION_MED
+}
+
+func (a *MedAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ var err error
+ switch a.action {
+ case MED_ACTION_MOD:
+ err = path.SetMed(a.value, false)
+ case MED_ACTION_REPLACE:
+ err = path.SetMed(a.value, true)
+ }
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Med Action",
+ "Error": err,
+ }).Warn("Could not set Med on path")
+ }
+ return path
+}
+
+func (a *MedAction) ToConfig() config.BgpSetMedType {
+ if a.action == MED_ACTION_MOD && a.value > 0 {
+ return config.BgpSetMedType(fmt.Sprintf("+%d", a.value))
+ }
+ return config.BgpSetMedType(fmt.Sprintf("%d", a.value))
+}
+
+func (a *MedAction) String() string {
+ return string(a.ToConfig())
+}
+
+func (a *MedAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+var _regexpParseMedAction = regexp.MustCompile(`^(\+|\-)?(\d+)$`)
+
+func NewMedAction(c config.BgpSetMedType) (*MedAction, error) {
+ if string(c) == "" {
+ return nil, nil
+ }
+
+ elems := _regexpParseMedAction.FindStringSubmatch(string(c))
+ if len(elems) != 3 {
+ return nil, fmt.Errorf("invalid med action format")
+ }
+ action := MED_ACTION_REPLACE
+ switch elems[1] {
+ case "+", "-":
+ action = MED_ACTION_MOD
+ }
+ value, _ := strconv.ParseInt(string(c), 10, 64)
+ return &MedAction{
+ value: value,
+ action: action,
+ }, nil
+}
+
+func NewMedActionFromApiStruct(action MedActionType, value int64) *MedAction {
+ return &MedAction{action: action, value: value}
+}
+
+type LocalPrefAction struct {
+ value uint32
+}
+
+func (a *LocalPrefAction) Type() ActionType {
+ return ACTION_LOCAL_PREF
+}
+
+func (a *LocalPrefAction) Apply(path *Path, _ *PolicyOptions) *Path {
+ path.setPathAttr(bgp.NewPathAttributeLocalPref(a.value))
+ return path
+}
+
+func (a *LocalPrefAction) ToConfig() uint32 {
+ return a.value
+}
+
+func (a *LocalPrefAction) String() string {
+ return fmt.Sprintf("%d", a.value)
+}
+
+func (a *LocalPrefAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewLocalPrefAction(value uint32) (*LocalPrefAction, error) {
+ if value == 0 {
+ return nil, nil
+ }
+ return &LocalPrefAction{
+ value: value,
+ }, nil
+}
+
+type AsPathPrependAction struct {
+ asn uint32
+ useLeftMost bool
+ repeat uint8
+}
+
+func (a *AsPathPrependAction) Type() ActionType {
+ return ACTION_AS_PATH_PREPEND
+}
+
+func (a *AsPathPrependAction) Apply(path *Path, option *PolicyOptions) *Path {
+ var asn uint32
+ if a.useLeftMost {
+ aspath := path.GetAsSeqList()
+ if len(aspath) == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "AsPathPrepend Action",
+ }).Warn("aspath length is zero.")
+ return path
+ }
+ asn = aspath[0]
+ if asn == 0 {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "AsPathPrepend Action",
+ }).Warn("left-most ASN is not seq")
+ return path
+ }
+ } else {
+ asn = a.asn
+ }
+
+ confed := option != nil && option.Info.Confederation
+ path.PrependAsn(asn, a.repeat, confed)
+
+ return path
+}
+
+func (a *AsPathPrependAction) ToConfig() *config.SetAsPathPrepend {
+ return &config.SetAsPathPrepend{
+ RepeatN: uint8(a.repeat),
+ As: func() string {
+ if a.useLeftMost {
+ return "last-as"
+ }
+ return fmt.Sprintf("%d", a.asn)
+ }(),
+ }
+}
+
+func (a *AsPathPrependAction) String() string {
+ c := a.ToConfig()
+ return fmt.Sprintf("prepend %s %d times", c.As, c.RepeatN)
+}
+
+func (a *AsPathPrependAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+// NewAsPathPrependAction creates AsPathPrependAction object.
+// If ASN cannot be parsed, nil will be returned.
+func NewAsPathPrependAction(action config.SetAsPathPrepend) (*AsPathPrependAction, error) {
+ a := &AsPathPrependAction{
+ repeat: action.RepeatN,
+ }
+ switch action.As {
+ case "":
+ if a.repeat == 0 {
+ return nil, nil
+ }
+ return nil, fmt.Errorf("specify as to prepend")
+ case "last-as":
+ a.useLeftMost = true
+ default:
+ asn, err := strconv.ParseUint(action.As, 10, 32)
+ if err != nil {
+ return nil, fmt.Errorf("As number string invalid")
+ }
+ a.asn = uint32(asn)
+ }
+ return a, nil
+}
+
+type NexthopAction struct {
+ value net.IP
+ self bool
+}
+
+func (a *NexthopAction) Type() ActionType {
+ return ACTION_NEXTHOP
+}
+
+func (a *NexthopAction) Apply(path *Path, options *PolicyOptions) *Path {
+ if a.self {
+ if options != nil && options.Info != nil && options.Info.LocalAddress != nil {
+ path.SetNexthop(options.Info.LocalAddress)
+ }
+ return path
+ }
+ path.SetNexthop(a.value)
+ return path
+}
+
+func (a *NexthopAction) ToConfig() config.BgpNextHopType {
+ if a.self {
+ return config.BgpNextHopType("self")
+ }
+ return config.BgpNextHopType(a.value.String())
+}
+
+func (a *NexthopAction) String() string {
+ return string(a.ToConfig())
+}
+
+func (a *NexthopAction) MarshalJSON() ([]byte, error) {
+ return json.Marshal(a.ToConfig())
+}
+
+func NewNexthopAction(c config.BgpNextHopType) (*NexthopAction, error) {
+ switch strings.ToLower(string(c)) {
+ case "":
+ return nil, nil
+ case "self":
+ return &NexthopAction{
+ self: true,
+ }, nil
+ }
+ addr := net.ParseIP(string(c))
+ if addr == nil {
+ return nil, fmt.Errorf("invalid ip address format: %s", string(c))
+ }
+ return &NexthopAction{
+ value: addr,
+ }, nil
+}
+
+type Statement struct {
+ Name string
+ Conditions []Condition
+ RouteAction Action
+ ModActions []Action
+}
+
+// evaluate each condition in the statement according to MatchSetOptions
+func (s *Statement) Evaluate(p *Path, options *PolicyOptions) bool {
+ for _, c := range s.Conditions {
+ if !c.Evaluate(p, options) {
+ return false
+ }
+ }
+ return true
+}
+
+func (s *Statement) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) {
+ result := s.Evaluate(path, options)
+ if result {
+ if len(s.ModActions) != 0 {
+ // apply all modification actions
+ path = path.Clone(path.IsWithdraw)
+ for _, action := range s.ModActions {
+ path = action.Apply(path, options)
+ }
+ }
+ //Routing action
+ if s.RouteAction == nil || reflect.ValueOf(s.RouteAction).IsNil() {
+ return ROUTE_TYPE_NONE, path
+ }
+ p := s.RouteAction.Apply(path, options)
+ if p == nil {
+ return ROUTE_TYPE_REJECT, path
+ }
+ return ROUTE_TYPE_ACCEPT, path
+ }
+ return ROUTE_TYPE_NONE, path
+}
+
+func (s *Statement) ToConfig() *config.Statement {
+ return &config.Statement{
+ Name: s.Name,
+ Conditions: func() config.Conditions {
+ cond := config.Conditions{}
+ for _, c := range s.Conditions {
+ switch c.(type) {
+ case *PrefixCondition:
+ v := c.(*PrefixCondition)
+ cond.MatchPrefixSet = config.MatchPrefixSet{PrefixSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()}
+ case *NeighborCondition:
+ v := c.(*NeighborCondition)
+ cond.MatchNeighborSet = config.MatchNeighborSet{NeighborSet: v.set.Name(), MatchSetOptions: v.option.ConvertToMatchSetOptionsRestrictedType()}
+ case *AsPathLengthCondition:
+ v := c.(*AsPathLengthCondition)
+ cond.BgpConditions.AsPathLength = config.AsPathLength{Operator: config.IntToAttributeComparisonMap[int(v.operator)], Value: v.length}
+ case *AsPathCondition:
+ v := c.(*AsPathCondition)
+ cond.BgpConditions.MatchAsPathSet = config.MatchAsPathSet{AsPathSet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *CommunityCondition:
+ v := c.(*CommunityCondition)
+ cond.BgpConditions.MatchCommunitySet = config.MatchCommunitySet{CommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ case *ExtCommunityCondition:
+ v := c.(*ExtCommunityCondition)
+ cond.BgpConditions.MatchExtCommunitySet = config.MatchExtCommunitySet{ExtCommunitySet: v.set.Name(), MatchSetOptions: config.IntToMatchSetOptionsTypeMap[int(v.option)]}
+ 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
+ case *RouteTypeCondition:
+ v := c.(*RouteTypeCondition)
+ cond.BgpConditions.RouteType = v.typ
+ case *AfiSafiInCondition:
+ v := c.(*AfiSafiInCondition)
+ res := make([]config.AfiSafiType, 0, len(v.routeFamilies))
+ for _, rf := range v.routeFamilies {
+ res = append(res, config.AfiSafiType(rf.String()))
+ }
+ cond.BgpConditions.AfiSafiInList = res
+ }
+ }
+ return cond
+ }(),
+ Actions: func() config.Actions {
+ act := config.Actions{}
+ if s.RouteAction != nil && !reflect.ValueOf(s.RouteAction).IsNil() {
+ a := s.RouteAction.(*RoutingAction)
+ if a.AcceptRoute {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_ACCEPT_ROUTE
+ } else {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_REJECT_ROUTE
+ }
+ } else {
+ act.RouteDisposition = config.ROUTE_DISPOSITION_NONE
+ }
+ for _, a := range s.ModActions {
+ switch a.(type) {
+ case *AsPathPrependAction:
+ act.BgpActions.SetAsPathPrepend = *a.(*AsPathPrependAction).ToConfig()
+ case *CommunityAction:
+ act.BgpActions.SetCommunity = *a.(*CommunityAction).ToConfig()
+ case *ExtCommunityAction:
+ act.BgpActions.SetExtCommunity = *a.(*ExtCommunityAction).ToConfig()
+ case *LargeCommunityAction:
+ act.BgpActions.SetLargeCommunity = *a.(*LargeCommunityAction).ToConfig()
+ case *MedAction:
+ act.BgpActions.SetMed = a.(*MedAction).ToConfig()
+ case *LocalPrefAction:
+ act.BgpActions.SetLocalPref = a.(*LocalPrefAction).ToConfig()
+ case *NexthopAction:
+ act.BgpActions.SetNextHop = a.(*NexthopAction).ToConfig()
+ }
+ }
+ return act
+ }(),
+ }
+}
+
+func (s *Statement) MarshalJSON() ([]byte, error) {
+ return json.Marshal(s.ToConfig())
+}
+
+type opType int
+
+const (
+ ADD opType = iota
+ REMOVE
+ REPLACE
+)
+
+func (lhs *Statement) mod(op opType, rhs *Statement) error {
+ cs := make([]Condition, len(lhs.Conditions))
+ copy(cs, lhs.Conditions)
+ ra := lhs.RouteAction
+ as := make([]Action, len(lhs.ModActions))
+ copy(as, lhs.ModActions)
+ for _, x := range rhs.Conditions {
+ var c Condition
+ i := 0
+ for idx, y := range lhs.Conditions {
+ if x.Type() == y.Type() {
+ c = y
+ i = idx
+ break
+ }
+ }
+ switch op {
+ case ADD:
+ if c != nil {
+ return fmt.Errorf("condition %d is already set", x.Type())
+ }
+ if cs == nil {
+ cs = make([]Condition, 0, len(rhs.Conditions))
+ }
+ cs = append(cs, x)
+ case REMOVE:
+ if c == nil {
+ return fmt.Errorf("condition %d is not set", x.Type())
+ }
+ cs = append(cs[:i], cs[i+1:]...)
+ if len(cs) == 0 {
+ cs = nil
+ }
+ case REPLACE:
+ if c == nil {
+ return fmt.Errorf("condition %d is not set", x.Type())
+ }
+ cs[i] = x
+ }
+ }
+ if rhs.RouteAction != nil && !reflect.ValueOf(rhs.RouteAction).IsNil() {
+ switch op {
+ case ADD:
+ if lhs.RouteAction != nil && !reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is already set")
+ }
+ ra = rhs.RouteAction
+ case REMOVE:
+ if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is not set")
+ }
+ ra = nil
+ case REPLACE:
+ if lhs.RouteAction == nil || reflect.ValueOf(lhs.RouteAction).IsNil() {
+ return fmt.Errorf("route action is not set")
+ }
+ ra = rhs.RouteAction
+ }
+ }
+ for _, x := range rhs.ModActions {
+ var a Action
+ i := 0
+ for idx, y := range lhs.ModActions {
+ if x.Type() == y.Type() {
+ a = y
+ i = idx
+ break
+ }
+ }
+ switch op {
+ case ADD:
+ if a != nil {
+ return fmt.Errorf("action %d is already set", x.Type())
+ }
+ if as == nil {
+ as = make([]Action, 0, len(rhs.ModActions))
+ }
+ as = append(as, x)
+ case REMOVE:
+ if a == nil {
+ return fmt.Errorf("action %d is not set", x.Type())
+ }
+ as = append(as[:i], as[i+1:]...)
+ if len(as) == 0 {
+ as = nil
+ }
+ case REPLACE:
+ if a == nil {
+ return fmt.Errorf("action %d is not set", x.Type())
+ }
+ as[i] = x
+ }
+ }
+ lhs.Conditions = cs
+ lhs.RouteAction = ra
+ lhs.ModActions = as
+ return nil
+}
+
+func (lhs *Statement) Add(rhs *Statement) error {
+ return lhs.mod(ADD, rhs)
+}
+
+func (lhs *Statement) Remove(rhs *Statement) error {
+ return lhs.mod(REMOVE, rhs)
+}
+
+func (lhs *Statement) Replace(rhs *Statement) error {
+ return lhs.mod(REPLACE, rhs)
+}
+
+func NewStatement(c config.Statement) (*Statement, error) {
+ if c.Name == "" {
+ return nil, fmt.Errorf("empty statement name")
+ }
+ var ra Action
+ var as []Action
+ var cs []Condition
+ var err error
+ cfs := []func() (Condition, error){
+ func() (Condition, error) {
+ return NewPrefixCondition(c.Conditions.MatchPrefixSet)
+ },
+ func() (Condition, error) {
+ return NewNeighborCondition(c.Conditions.MatchNeighborSet)
+ },
+ func() (Condition, error) {
+ return NewAsPathLengthCondition(c.Conditions.BgpConditions.AsPathLength)
+ },
+ func() (Condition, error) {
+ return NewRpkiValidationCondition(c.Conditions.BgpConditions.RpkiValidationResult)
+ },
+ func() (Condition, error) {
+ return NewRouteTypeCondition(c.Conditions.BgpConditions.RouteType)
+ },
+ func() (Condition, error) {
+ return NewAsPathCondition(c.Conditions.BgpConditions.MatchAsPathSet)
+ },
+ func() (Condition, error) {
+ return NewCommunityCondition(c.Conditions.BgpConditions.MatchCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewExtCommunityCondition(c.Conditions.BgpConditions.MatchExtCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewLargeCommunityCondition(c.Conditions.BgpConditions.MatchLargeCommunitySet)
+ },
+ func() (Condition, error) {
+ return NewNextHopCondition(c.Conditions.BgpConditions.NextHopInList)
+ },
+ func() (Condition, error) {
+ return NewAfiSafiInCondition(c.Conditions.BgpConditions.AfiSafiInList)
+ },
+ }
+ cs = make([]Condition, 0, len(cfs))
+ for _, f := range cfs {
+ c, err := f()
+ if err != nil {
+ return nil, err
+ }
+ if !reflect.ValueOf(c).IsNil() {
+ cs = append(cs, c)
+ }
+ }
+ ra, err = NewRoutingAction(c.Actions.RouteDisposition)
+ if err != nil {
+ return nil, err
+ }
+ afs := []func() (Action, error){
+ func() (Action, error) {
+ return NewCommunityAction(c.Actions.BgpActions.SetCommunity)
+ },
+ func() (Action, error) {
+ return NewExtCommunityAction(c.Actions.BgpActions.SetExtCommunity)
+ },
+ func() (Action, error) {
+ return NewLargeCommunityAction(c.Actions.BgpActions.SetLargeCommunity)
+ },
+ func() (Action, error) {
+ return NewMedAction(c.Actions.BgpActions.SetMed)
+ },
+ func() (Action, error) {
+ return NewLocalPrefAction(c.Actions.BgpActions.SetLocalPref)
+ },
+ func() (Action, error) {
+ return NewAsPathPrependAction(c.Actions.BgpActions.SetAsPathPrepend)
+ },
+ func() (Action, error) {
+ return NewNexthopAction(c.Actions.BgpActions.SetNextHop)
+ },
+ }
+ as = make([]Action, 0, len(afs))
+ for _, f := range afs {
+ a, err := f()
+ if err != nil {
+ return nil, err
+ }
+ if !reflect.ValueOf(a).IsNil() {
+ as = append(as, a)
+ }
+ }
+ return &Statement{
+ Name: c.Name,
+ Conditions: cs,
+ RouteAction: ra,
+ ModActions: as,
+ }, nil
+}
+
+type Policy struct {
+ Name string
+ Statements []*Statement
+}
+
+// Compare path with a policy's condition in stored order in the policy.
+// If a condition match, then this function stops evaluation and
+// subsequent conditions are skipped.
+func (p *Policy) Apply(path *Path, options *PolicyOptions) (RouteType, *Path) {
+ for _, stmt := range p.Statements {
+ var result RouteType
+ result, path = stmt.Apply(path, options)
+ if result != ROUTE_TYPE_NONE {
+ return result, path
+ }
+ }
+ return ROUTE_TYPE_NONE, path
+}
+
+func (p *Policy) ToConfig() *config.PolicyDefinition {
+ ss := make([]config.Statement, 0, len(p.Statements))
+ for _, s := range p.Statements {
+ ss = append(ss, *s.ToConfig())
+ }
+ return &config.PolicyDefinition{
+ Name: p.Name,
+ Statements: ss,
+ }
+}
+
+func (p *Policy) FillUp(m map[string]*Statement) error {
+ stmts := make([]*Statement, 0, len(p.Statements))
+ for _, x := range p.Statements {
+ y, ok := m[x.Name]
+ if !ok {
+ return fmt.Errorf("not found statement %s", x.Name)
+ }
+ stmts = append(stmts, y)
+ }
+ p.Statements = stmts
+ return nil
+}
+
+func (lhs *Policy) Add(rhs *Policy) error {
+ lhs.Statements = append(lhs.Statements, rhs.Statements...)
+ return nil
+}
+
+func (lhs *Policy) Remove(rhs *Policy) error {
+ stmts := make([]*Statement, 0, len(lhs.Statements))
+ for _, x := range lhs.Statements {
+ found := false
+ for _, y := range rhs.Statements {
+ if x.Name == y.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ stmts = append(stmts, x)
+ }
+ }
+ lhs.Statements = stmts
+ return nil
+}
+
+func (lhs *Policy) Replace(rhs *Policy) error {
+ lhs.Statements = rhs.Statements
+ return nil
+}
+
+func (p *Policy) MarshalJSON() ([]byte, error) {
+ return json.Marshal(p.ToConfig())
+}
+
+func NewPolicy(c config.PolicyDefinition) (*Policy, error) {
+ if c.Name == "" {
+ return nil, fmt.Errorf("empty policy name")
+ }
+ var st []*Statement
+ stmts := c.Statements
+ if len(stmts) != 0 {
+ st = make([]*Statement, 0, len(stmts))
+ for idx, stmt := range stmts {
+ if stmt.Name == "" {
+ stmt.Name = fmt.Sprintf("%s_stmt%d", c.Name, idx)
+ }
+ s, err := NewStatement(stmt)
+ if err != nil {
+ return nil, err
+ }
+ st = append(st, s)
+ }
+ }
+ return &Policy{
+ Name: c.Name,
+ Statements: st,
+ }, nil
+}
+
+type Policies []*Policy
+
+func (p Policies) Len() int {
+ return len(p)
+}
+
+func (p Policies) Swap(i, j int) {
+ p[i], p[j] = p[j], p[i]
+}
+
+func (p Policies) Less(i, j int) bool {
+ return p[i].Name < p[j].Name
+}
+
+type Assignment struct {
+ inPolicies []*Policy
+ defaultInPolicy RouteType
+ importPolicies []*Policy
+ defaultImportPolicy RouteType
+ exportPolicies []*Policy
+ defaultExportPolicy RouteType
+}
+
+type RoutingPolicy struct {
+ definedSetMap DefinedSetMap
+ policyMap map[string]*Policy
+ statementMap map[string]*Statement
+ assignmentMap map[string]*Assignment
+ mu sync.RWMutex
+}
+
+func (r *RoutingPolicy) ApplyPolicy(id string, dir PolicyDirection, before *Path, options *PolicyOptions) *Path {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if before == nil {
+ return nil
+ }
+
+ if before.IsWithdraw {
+ return before
+ }
+ result := ROUTE_TYPE_NONE
+ after := before
+ for _, p := range r.getPolicy(id, dir) {
+ result, after = p.Apply(after, options)
+ if result != ROUTE_TYPE_NONE {
+ break
+ }
+ }
+ if result == ROUTE_TYPE_NONE {
+ result = r.getDefaultPolicy(id, dir)
+ }
+ switch result {
+ case ROUTE_TYPE_ACCEPT:
+ return after
+ default:
+ return nil
+ }
+}
+
+func (r *RoutingPolicy) getPolicy(id string, dir PolicyDirection) []*Policy {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ return nil
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ return a.inPolicies
+ case POLICY_DIRECTION_IMPORT:
+ return a.importPolicies
+ case POLICY_DIRECTION_EXPORT:
+ return a.exportPolicies
+ default:
+ return nil
+ }
+}
+
+func (r *RoutingPolicy) getDefaultPolicy(id string, dir PolicyDirection) RouteType {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ return ROUTE_TYPE_NONE
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ return a.defaultInPolicy
+ case POLICY_DIRECTION_IMPORT:
+ return a.defaultImportPolicy
+ case POLICY_DIRECTION_EXPORT:
+ return a.defaultExportPolicy
+ default:
+ return ROUTE_TYPE_NONE
+ }
+
+}
+
+func (r *RoutingPolicy) setPolicy(id string, dir PolicyDirection, policies []*Policy) error {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ a = &Assignment{}
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ a.inPolicies = policies
+ case POLICY_DIRECTION_IMPORT:
+ a.importPolicies = policies
+ case POLICY_DIRECTION_EXPORT:
+ a.exportPolicies = policies
+ }
+ r.assignmentMap[id] = a
+ return nil
+}
+
+func (r *RoutingPolicy) setDefaultPolicy(id string, dir PolicyDirection, typ RouteType) error {
+ a, ok := r.assignmentMap[id]
+ if !ok {
+ a = &Assignment{}
+ }
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ a.defaultInPolicy = typ
+ case POLICY_DIRECTION_IMPORT:
+ a.defaultImportPolicy = typ
+ case POLICY_DIRECTION_EXPORT:
+ a.defaultExportPolicy = typ
+ }
+ r.assignmentMap[id] = a
+ return nil
+}
+
+func (r *RoutingPolicy) getAssignmentFromConfig(dir PolicyDirection, a config.ApplyPolicy) ([]*Policy, RouteType, error) {
+ var names []string
+ var cdef config.DefaultPolicyType
+ def := ROUTE_TYPE_ACCEPT
+ c := a.Config
+ switch dir {
+ case POLICY_DIRECTION_IN:
+ names = c.InPolicyList
+ cdef = c.DefaultInPolicy
+ case POLICY_DIRECTION_IMPORT:
+ names = c.ImportPolicyList
+ cdef = c.DefaultImportPolicy
+ case POLICY_DIRECTION_EXPORT:
+ names = c.ExportPolicyList
+ cdef = c.DefaultExportPolicy
+ default:
+ return nil, def, fmt.Errorf("invalid policy direction")
+ }
+ if cdef == config.DEFAULT_POLICY_TYPE_REJECT_ROUTE {
+ def = ROUTE_TYPE_REJECT
+ }
+ ps := make([]*Policy, 0, len(names))
+ seen := make(map[string]bool)
+ for _, name := range names {
+ p, ok := r.policyMap[name]
+ if !ok {
+ return nil, def, fmt.Errorf("not found policy %s", name)
+ }
+ if seen[name] {
+ return nil, def, fmt.Errorf("duplicated policy %s", name)
+ }
+ seen[name] = true
+ ps = append(ps, p)
+ }
+ return ps, def, nil
+}
+
+func (r *RoutingPolicy) validateCondition(v Condition) (err error) {
+ switch v.Type() {
+ case CONDITION_PREFIX:
+ m := r.definedSetMap[DEFINED_TYPE_PREFIX]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found prefix set %s", v.Name())
+ } else {
+ c := v.(*PrefixCondition)
+ c.set = i.(*PrefixSet)
+ }
+ case CONDITION_NEIGHBOR:
+ m := r.definedSetMap[DEFINED_TYPE_NEIGHBOR]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found neighbor set %s", v.Name())
+ } else {
+ c := v.(*NeighborCondition)
+ c.set = i.(*NeighborSet)
+ }
+ case CONDITION_AS_PATH:
+ m := r.definedSetMap[DEFINED_TYPE_AS_PATH]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found as path set %s", v.Name())
+ } else {
+ c := v.(*AsPathCondition)
+ c.set = i.(*AsPathSet)
+ }
+ case CONDITION_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found community set %s", v.Name())
+ } else {
+ c := v.(*CommunityCondition)
+ c.set = i.(*CommunitySet)
+ }
+ case CONDITION_EXT_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_EXT_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found ext-community set %s", v.Name())
+ } else {
+ c := v.(*ExtCommunityCondition)
+ c.set = i.(*ExtCommunitySet)
+ }
+ case CONDITION_LARGE_COMMUNITY:
+ m := r.definedSetMap[DEFINED_TYPE_LARGE_COMMUNITY]
+ if i, ok := m[v.Name()]; !ok {
+ return fmt.Errorf("not found large-community set %s", v.Name())
+ } else {
+ c := v.(*LargeCommunityCondition)
+ c.set = i.(*LargeCommunitySet)
+ }
+ case CONDITION_NEXT_HOP:
+ case CONDITION_AFI_SAFI_IN:
+ case CONDITION_AS_PATH_LENGTH:
+ case CONDITION_RPKI:
+ }
+ return nil
+}
+
+func (r *RoutingPolicy) inUse(d DefinedSet) bool {
+ name := d.Name()
+ for _, p := range r.policyMap {
+ for _, s := range p.Statements {
+ for _, c := range s.Conditions {
+ if c.Set() != nil && c.Set().Name() == name {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func (r *RoutingPolicy) statementInUse(x *Statement) bool {
+ for _, p := range r.policyMap {
+ for _, y := range p.Statements {
+ if x.Name == y.Name {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func (r *RoutingPolicy) reload(c config.RoutingPolicy) error {
+ dmap := make(map[DefinedType]map[string]DefinedSet)
+ dmap[DEFINED_TYPE_PREFIX] = make(map[string]DefinedSet)
+ d := c.DefinedSets
+ for _, x := range d.PrefixSets {
+ y, err := NewPrefixSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty prefix set")
+ }
+ dmap[DEFINED_TYPE_PREFIX][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_NEIGHBOR] = make(map[string]DefinedSet)
+ for _, x := range d.NeighborSets {
+ y, err := NewNeighborSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty neighbor set")
+ }
+ dmap[DEFINED_TYPE_NEIGHBOR][y.Name()] = y
+ }
+ // dmap[DEFINED_TYPE_TAG] = make(map[string]DefinedSet)
+ // for _, x := range c.DefinedSets.TagSets{
+ // y, err := NewTagSet(x)
+ // if err != nil {
+ // return nil, err
+ // }
+ // dmap[DEFINED_TYPE_TAG][y.Name()] = y
+ // }
+ bd := c.DefinedSets.BgpDefinedSets
+ dmap[DEFINED_TYPE_AS_PATH] = make(map[string]DefinedSet)
+ for _, x := range bd.AsPathSets {
+ y, err := NewAsPathSet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty as path set")
+ }
+ dmap[DEFINED_TYPE_AS_PATH][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.CommunitySets {
+ y, err := NewCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty community set")
+ }
+ dmap[DEFINED_TYPE_COMMUNITY][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_EXT_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.ExtCommunitySets {
+ y, err := NewExtCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty ext-community set")
+ }
+ dmap[DEFINED_TYPE_EXT_COMMUNITY][y.Name()] = y
+ }
+ dmap[DEFINED_TYPE_LARGE_COMMUNITY] = make(map[string]DefinedSet)
+ for _, x := range bd.LargeCommunitySets {
+ y, err := NewLargeCommunitySet(x)
+ if err != nil {
+ return err
+ }
+ if y == nil {
+ return fmt.Errorf("empty large-community set")
+ }
+ dmap[DEFINED_TYPE_LARGE_COMMUNITY][y.Name()] = y
+ }
+
+ pmap := make(map[string]*Policy)
+ smap := make(map[string]*Statement)
+ for _, x := range c.PolicyDefinitions {
+ y, err := NewPolicy(x)
+ if err != nil {
+ return err
+ }
+ if _, ok := pmap[y.Name]; ok {
+ return fmt.Errorf("duplicated policy name. policy name must be unique.")
+ }
+ pmap[y.Name] = y
+ for _, s := range y.Statements {
+ _, ok := smap[s.Name]
+ if ok {
+ return fmt.Errorf("duplicated statement name. statement name must be unique.")
+ }
+ smap[s.Name] = s
+ }
+ }
+
+ // hacky
+ oldMap := r.definedSetMap
+ r.definedSetMap = dmap
+ for _, y := range pmap {
+ for _, s := range y.Statements {
+ for _, c := range s.Conditions {
+ if err := r.validateCondition(c); err != nil {
+ r.definedSetMap = oldMap
+ return err
+ }
+ }
+ }
+ }
+
+ r.definedSetMap = dmap
+ r.policyMap = pmap
+ r.statementMap = smap
+ r.assignmentMap = make(map[string]*Assignment)
+ // allow all routes coming in and going out by default
+ r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_IMPORT, ROUTE_TYPE_ACCEPT)
+ r.setDefaultPolicy(GLOBAL_RIB_NAME, POLICY_DIRECTION_EXPORT, ROUTE_TYPE_ACCEPT)
+ return nil
+}
+
+func (r *RoutingPolicy) GetDefinedSet(typ DefinedType, name string) (*config.DefinedSets, error) {
+ r.mu.RLock()
+
+ set, ok := r.definedSetMap[typ]
+ if !ok {
+ return nil, fmt.Errorf("invalid defined-set type: %d", typ)
+ }
+
+ var dl DefinedSetList
+ for _, s := range set {
+ dl = append(dl, s)
+ }
+ r.mu.RUnlock()
+
+ sort.Sort(dl)
+
+ sets := &config.DefinedSets{
+ PrefixSets: make([]config.PrefixSet, 0),
+ NeighborSets: make([]config.NeighborSet, 0),
+ BgpDefinedSets: config.BgpDefinedSets{
+ CommunitySets: make([]config.CommunitySet, 0),
+ ExtCommunitySets: make([]config.ExtCommunitySet, 0),
+ LargeCommunitySets: make([]config.LargeCommunitySet, 0),
+ AsPathSets: make([]config.AsPathSet, 0),
+ },
+ }
+ for _, s := range dl {
+ if name != "" && s.Name() != name {
+ continue
+ }
+ switch s.(type) {
+ case *PrefixSet:
+ sets.PrefixSets = append(sets.PrefixSets, *s.(*PrefixSet).ToConfig())
+ case *NeighborSet:
+ sets.NeighborSets = append(sets.NeighborSets, *s.(*NeighborSet).ToConfig())
+ case *CommunitySet:
+ sets.BgpDefinedSets.CommunitySets = append(sets.BgpDefinedSets.CommunitySets, *s.(*CommunitySet).ToConfig())
+ case *ExtCommunitySet:
+ sets.BgpDefinedSets.ExtCommunitySets = append(sets.BgpDefinedSets.ExtCommunitySets, *s.(*ExtCommunitySet).ToConfig())
+ case *LargeCommunitySet:
+ sets.BgpDefinedSets.LargeCommunitySets = append(sets.BgpDefinedSets.LargeCommunitySets, *s.(*LargeCommunitySet).ToConfig())
+ case *AsPathSet:
+ sets.BgpDefinedSets.AsPathSets = append(sets.BgpDefinedSets.AsPathSets, *s.(*AsPathSet).ToConfig())
+ }
+ }
+ return sets, nil
+}
+
+func (r *RoutingPolicy) AddDefinedSet(s DefinedSet) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[s.Type()]; !ok {
+ return fmt.Errorf("invalid defined-set type: %d", s.Type())
+ } else {
+ if d, ok := m[s.Name()]; ok {
+ if err := d.Append(s); err != nil {
+ return err
+ }
+ } else {
+ m[s.Name()] = s
+ }
+ }
+ return nil
+}
+
+func (r *RoutingPolicy) DeleteDefinedSet(a DefinedSet, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[a.Type()]; !ok {
+ err = fmt.Errorf("invalid defined-set type: %d", a.Type())
+ } else {
+ d, ok := m[a.Name()]
+ if !ok {
+ return fmt.Errorf("not found defined-set: %s", a.Name())
+ }
+ if all {
+ if r.inUse(d) {
+ err = fmt.Errorf("can't delete. defined-set %s is in use", a.Name())
+ } else {
+ delete(m, a.Name())
+ }
+ } else {
+ err = d.Remove(a)
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplaceDefinedSet(a DefinedSet) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if m, ok := r.definedSetMap[a.Type()]; !ok {
+ err = fmt.Errorf("invalid defined-set type: %d", a.Type())
+ } else {
+ if d, ok := m[a.Name()]; !ok {
+ err = fmt.Errorf("not found defined-set: %s", a.Name())
+ } else {
+ err = d.Replace(a)
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetStatement() []*config.Statement {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ l := make([]*config.Statement, 0, len(r.statementMap))
+ for _, st := range r.statementMap {
+ l = append(l, st.ToConfig())
+ }
+ return l
+}
+
+func (r *RoutingPolicy) AddStatement(st *Statement) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ err = d.Add(st)
+ } else {
+ m[name] = st
+ }
+
+ return err
+}
+
+func (r *RoutingPolicy) DeleteStatement(st *Statement, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ if all {
+ if r.statementInUse(d) {
+ err = fmt.Errorf("can't delete. statement %s is in use", name)
+ } else {
+ delete(m, name)
+ }
+ } else {
+ err = d.Remove(st)
+ }
+ } else {
+ err = fmt.Errorf("not found statement: %s", name)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplaceStatement(st *Statement) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ m := r.statementMap
+ name := st.Name
+ if d, ok := m[name]; ok {
+ err = d.Replace(st)
+ } else {
+ err = fmt.Errorf("not found statement: %s", name)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetAllPolicy() []*config.PolicyDefinition {
+ r.mu.RLock()
+
+ var ps Policies
+ for _, p := range r.policyMap {
+ ps = append(ps, p)
+ }
+ r.mu.RUnlock()
+
+ sort.Sort(ps)
+
+ l := make([]*config.PolicyDefinition, 0, len(ps))
+ for _, p := range ps {
+ l = append(l, p.ToConfig())
+ }
+ return l
+}
+
+func (r *RoutingPolicy) AddPolicy(x *Policy, refer bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, st := range x.Statements {
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ }
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if refer {
+ err = x.FillUp(sMap)
+ } else {
+ for _, st := range x.Statements {
+ if _, ok := sMap[st.Name]; ok {
+ err = fmt.Errorf("statement %s already defined", st.Name)
+ return
+ }
+ sMap[st.Name] = st
+ }
+ }
+ if ok {
+ err = y.Add(x)
+ } else {
+ pMap[name] = x
+ }
+
+ return err
+}
+
+func (r *RoutingPolicy) DeletePolicy(x *Policy, all, preserve bool, activeId []string) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if !ok {
+ err = fmt.Errorf("not found policy: %s", name)
+ return
+ }
+ inUse := func(ids []string) bool {
+ for _, id := range ids {
+ for _, dir := range []PolicyDirection{POLICY_DIRECTION_IN, POLICY_DIRECTION_EXPORT, POLICY_DIRECTION_EXPORT} {
+ for _, y := range r.getPolicy(id, dir) {
+ if x.Name == y.Name {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+
+ if all {
+ if inUse(activeId) {
+ err = fmt.Errorf("can't delete. policy %s is in use", name)
+ return
+ }
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": name,
+ }).Debug("delete policy")
+ delete(pMap, name)
+ } else {
+ err = y.Remove(x)
+ }
+ if err == nil && !preserve {
+ for _, st := range y.Statements {
+ if !r.statementInUse(st) {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": st.Name,
+ }).Debug("delete unused statement")
+ delete(sMap, st.Name)
+ }
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplacePolicy(x *Policy, refer, preserve bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ for _, st := range x.Statements {
+ for _, c := range st.Conditions {
+ if err = r.validateCondition(c); err != nil {
+ return
+ }
+ }
+ }
+
+ pMap := r.policyMap
+ sMap := r.statementMap
+ name := x.Name
+ y, ok := pMap[name]
+ if !ok {
+ err = fmt.Errorf("not found policy: %s", name)
+ return
+ }
+ if refer {
+ if err = x.FillUp(sMap); err != nil {
+ return
+ }
+ } else {
+ for _, st := range x.Statements {
+ if _, ok := sMap[st.Name]; ok {
+ err = fmt.Errorf("statement %s already defined", st.Name)
+ return
+ }
+ sMap[st.Name] = st
+ }
+ }
+
+ ys := y.Statements
+ err = y.Replace(x)
+ if err == nil && !preserve {
+ for _, st := range ys {
+ if !r.statementInUse(st) {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Key": st.Name,
+ }).Debug("delete unused statement")
+ delete(sMap, st.Name)
+ }
+ }
+ }
+ return err
+}
+
+func (r *RoutingPolicy) GetPolicyAssignment(id string, dir PolicyDirection) (RouteType, []*config.PolicyDefinition, error) {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ rt := r.getDefaultPolicy(id, dir)
+
+ ps := r.getPolicy(id, dir)
+ l := make([]*config.PolicyDefinition, 0, len(ps))
+ for _, p := range ps {
+ l = append(l, p.ToConfig())
+ }
+ return rt, l, nil
+}
+
+func (r *RoutingPolicy) AddPolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ cur := r.getPolicy(id, dir)
+ if cur == nil {
+ err = r.setPolicy(id, dir, ps)
+ } else {
+ seen = make(map[string]bool)
+ ps = append(cur, ps...)
+ for _, x := range ps {
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ }
+ err = r.setPolicy(id, dir, ps)
+ }
+ if err == nil && def != ROUTE_TYPE_NONE {
+ err = r.setDefaultPolicy(id, dir, def)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) DeletePolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, all bool) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ cur := r.getPolicy(id, dir)
+
+ if all {
+ err = r.setPolicy(id, dir, nil)
+ if err != nil {
+ return
+ }
+ err = r.setDefaultPolicy(id, dir, ROUTE_TYPE_NONE)
+ } else {
+ l := len(cur) - len(ps)
+ if l < 0 {
+ // try to remove more than the assigned policies...
+ l = len(cur)
+ }
+ n := make([]*Policy, 0, l)
+ for _, y := range cur {
+ found := false
+ for _, x := range ps {
+ if x.Name == y.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ n = append(n, y)
+ }
+ }
+ err = r.setPolicy(id, dir, n)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) ReplacePolicyAssignment(id string, dir PolicyDirection, policies []*config.PolicyDefinition, def RouteType) (err error) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ ps := make([]*Policy, 0, len(policies))
+ seen := make(map[string]bool)
+ for _, x := range policies {
+ p, ok := r.policyMap[x.Name]
+ if !ok {
+ err = fmt.Errorf("not found policy %s", x.Name)
+ return
+ }
+ if seen[x.Name] {
+ err = fmt.Errorf("duplicated policy %s", x.Name)
+ return
+ }
+ seen[x.Name] = true
+ ps = append(ps, p)
+ }
+ r.getPolicy(id, dir)
+ err = r.setPolicy(id, dir, ps)
+ if err == nil && def != ROUTE_TYPE_NONE {
+ err = r.setDefaultPolicy(id, dir, def)
+ }
+ return err
+}
+
+func (r *RoutingPolicy) Reset(rp *config.RoutingPolicy, ap map[string]config.ApplyPolicy) error {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ if rp != nil {
+ if err := r.reload(*rp); err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ }).Errorf("failed to create routing policy: %s", err)
+ return err
+ }
+ }
+
+ for id, c := range ap {
+ for _, dir := range []PolicyDirection{POLICY_DIRECTION_IN, POLICY_DIRECTION_IMPORT, POLICY_DIRECTION_EXPORT} {
+ ps, def, err := r.getAssignmentFromConfig(dir, c)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Dir": dir,
+ }).Errorf("failed to get policy info: %s", err)
+ continue
+ }
+ r.setDefaultPolicy(id, dir, def)
+ r.setPolicy(id, dir, ps)
+ }
+ }
+ return nil
+}
+
+func NewRoutingPolicy() *RoutingPolicy {
+ return &RoutingPolicy{
+ definedSetMap: make(map[DefinedType]map[string]DefinedSet),
+ policyMap: make(map[string]*Policy),
+ statementMap: make(map[string]*Statement),
+ assignmentMap: make(map[string]*Assignment),
+ }
+}
+
+func CanImportToVrf(v *Vrf, path *Path) bool {
+ f := func(arg []bgp.ExtendedCommunityInterface) []string {
+ ret := make([]string, 0, len(arg))
+ for _, a := range arg {
+ ret = append(ret, fmt.Sprintf("RT:%s", a.String()))
+ }
+ return ret
+ }
+ set, _ := NewExtCommunitySet(config.ExtCommunitySet{
+ ExtCommunitySetName: v.Name,
+ ExtCommunityList: f(v.ImportRt),
+ })
+ matchSet := config.MatchExtCommunitySet{
+ ExtCommunitySet: v.Name,
+ MatchSetOptions: config.MATCH_SET_OPTIONS_TYPE_ANY,
+ }
+ c, _ := NewExtCommunityCondition(matchSet)
+ c.set = set
+ return c.Evaluate(path, nil)
+}
+
+type PolicyAssignment struct {
+ Name string
+ Type PolicyDirection
+ Policies []*Policy
+ Default RouteType
+}