2017-07-12 21:54:38 -05:00

3698 lines
86 KiB
Go

// 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"
"strconv"
"strings"
"sync"
"github.com/armon/go-radix"
"github.com/osrg/gobgp/config"
"github.com/osrg/gobgp/packet/bgp"
log "github.com/sirupsen/logrus"
)
type PolicyOptions struct {
Info *PeerInfo
}
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
)
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
)
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()
}
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
} else {
exp := regexp.MustCompile("(\\d+)\\.\\.(\\d+)")
elems := exp.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.Atoi(elems[1])
max, _ := strconv.Atoi(elems[2])
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 NeighborSet struct {
name string
list []net.IP
}
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.IP, 0, len(lhs.list))
for _, x := range lhs.list {
found := false
for _, y := range rhs.list {
if x.Equal(y) {
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.IP) (*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.IP, 0, len(c.NeighborInfoList))
for _, x := range c.NeighborInfoList {
addr := net.ParseIP(x)
if addr == nil {
return nil, fmt.Errorf("invalid address: %s", x)
}
list = append(list, addr)
}
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
}
func NewSingleAsPathMatch(arg string) *singleAsPathMatch {
leftMostRe := regexp.MustCompile("$\\^([0-9]+)_^")
originRe := regexp.MustCompile("^_([0-9]+)\\$$")
includeRe := regexp.MustCompile("^_([0-9]+)_$")
onlyRe := regexp.MustCompile("^\\^([0-9]+)\\$$")
switch {
case leftMostRe.MatchString(arg):
asn, _ := strconv.Atoi(leftMostRe.FindStringSubmatch(arg)[1])
return &singleAsPathMatch{
asn: uint32(asn),
mode: LEFT_MOST,
}
case originRe.MatchString(arg):
asn, _ := strconv.Atoi(originRe.FindStringSubmatch(arg)[1])
return &singleAsPathMatch{
asn: uint32(asn),
mode: ORIGIN,
}
case includeRe.MatchString(arg):
asn, _ := strconv.Atoi(includeRe.FindStringSubmatch(arg)[1])
return &singleAsPathMatch{
asn: uint32(asn),
mode: INCLUDE,
}
case onlyRe.MatchString(arg):
asn, _ := strconv.Atoi(onlyRe.FindStringSubmatch(arg)[1])
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())
}
func ParseCommunity(arg string) (uint32, error) {
i, err := strconv.ParseUint(arg, 10, 32)
if err == nil {
return uint32(i), nil
}
exp := regexp.MustCompile("(\\d+):(\\d+)")
elems := exp.FindStringSubmatch(arg)
if len(elems) == 3 {
fst, _ := strconv.Atoi(elems[1])
snd, _ := strconv.Atoi(elems[2])
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)
}
func ParseCommunityRegexp(arg string) (*regexp.Regexp, error) {
i, err := strconv.ParseUint(arg, 10, 32)
if err == nil {
return regexp.MustCompile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff)), nil
}
if regexp.MustCompile("(\\d+.)*\\d+:\\d+").MatchString(arg) {
return regexp.MustCompile(fmt.Sprintf("^%s$", arg)), nil
}
for i, v := range bgp.WellKnownCommunityNameMap {
if strings.Replace(strings.ToLower(arg), "_", "-", -1) == v {
return regexp.MustCompile(fmt.Sprintf("^%d:%d$", i>>16, i&0x0000ffff)), nil
}
}
exp, err := regexp.Compile(arg)
if err != nil {
return nil, fmt.Errorf("invalid community format: %s", arg)
}
return exp, nil
}
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())
}
func ParseLargeCommunityRegexp(arg string) (*regexp.Regexp, error) {
if regexp.MustCompile("\\d+:\\d+:\\d+").MatchString(arg) {
return regexp.MustCompile(fmt.Sprintf("^%s$", arg)), nil
}
exp, err := regexp.Compile(arg)
if err != nil {
return nil, fmt.Errorf("invalid large-community format: %s", arg)
}
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 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 neighbor.Equal(n) {
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, _ *PolicyOptions) bool {
return c.result == path.Validation()
}
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 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 == false {
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 == false {
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 == false {
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())
}
func (a *CommunityAction) String() string {
list := a.ToConfig().SetCommunityMethod.CommunitiesList
exp := regexp.MustCompile("[\\^\\$]")
l := exp.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
exp := regexp.MustCompile("[\\^\\$]")
l := exp.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
exp := regexp.MustCompile("[\\^\\$]")
l := exp.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())
}
func NewMedAction(c config.BgpSetMedType) (*MedAction, error) {
if string(c) == "" {
return nil, nil
}
exp := regexp.MustCompile("^(\\+|\\-)?(\\d+)$")
elems := exp.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, _ *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
}
path.PrependAsn(asn, a.repeat)
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 *RpkiValidationCondition:
v := c.(*RpkiValidationCondition)
cond.BgpConditions.RpkiValidationResult = v.result
case *RouteTypeCondition:
v := c.(*RouteTypeCondition)
cond.BgpConditions.RouteType = v.typ
}
}
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)
},
}
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 filtered := before.Filtered(id); filtered > POLICY_DIRECTION_NONE && filtered < dir {
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_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().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()
defer r.mu.RUnlock()
set, ok := r.definedSetMap[typ]
if !ok {
return nil, fmt.Errorf("invalid defined-set type: %d", typ)
}
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 set {
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()
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()
defer r.mu.RUnlock()
l := make([]*config.PolicyDefinition, 0, len(r.policyMap))
for _, p := range r.policyMap {
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
}
}
err = y.Replace(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) 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
}