Files
osrg-gobgp/table/policy.go
T
ISHIDA Wataru 40ce51137a table: remove unnecessary copy of path attributes
the result of memory profile (500 route-server-clients each of them advertises
100 routes)

before:
(pprof) top5
9330.48MB of 9367.53MB total (99.60%)
Dropped 157 nodes (cum <= 46.84MB)
Showing top 10 nodes out of 17 (cum >= 9334.17MB)
flat       flat%   sum%        cum   cum%
6163.04MB 65.79% 65.79%  6163.04MB 65.79% github.com/osrg/gobgp/table.NewPath
1155.05MB 12.33% 78.12%  7302.59MB 77.96% github.com/osrg/gobgp/table.(*Path).Clone
986.31MB  10.53% 88.65%  1388.81MB 14.83% github.com/osrg/gobgp/table.(*AdjRib).Update
402.51MB   4.30% 92.95%   402.51MB  4.30%  fmt.Sprintf
402.51MB   4.30% 97.24%   402.51MB  4.30% net.parseIPv4

after:
(pprof) top
3913.02MB of 3978.69MB total (98.35%)
Dropped 148 nodes (cum <= 19.89MB)
Showing top 10 nodes out of 11 (cum >= 21MB)
flat       flat%   sum%        cum   cum%
2970.30MB 74.66% 74.66%  2975.80MB 74.79% github.com/osrg/gobgp/server.filterpath
810.09MB  20.36% 95.02%   810.59MB 20.37% github.com/osrg/gobgp/table.(*AdjRib).Update
115.60MB   2.91% 97.92%   119.10MB  2.99% github.com/osrg/gobgp/table.createUpdateMsgFromPath
10MB       0.25% 98.17%  1878.02MB 47.20% github.com/osrg/gobgp/server.(*BgpServer).propagateUpdate
4.50MB     0.11% 98.29%   144.60MB  3.63% github.com/osrg/gobgp/table.CreateUpdateMsgFromPaths

Signed-off-by: ISHIDA Wataru <[email protected]>
2016-01-16 18:22:13 +09:00

2852 lines
66 KiB
Go

// Copyright (C) 2014,2015 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"
"fmt"
"net"
"reflect"
"regexp"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/armon/go-radix"
api "github.com/osrg/gobgp/api"
"github.com/osrg/gobgp/config"
"github.com/osrg/gobgp/packet"
)
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
)
type RouteType int
const (
ROUTE_TYPE_NONE RouteType = iota
ROUTE_TYPE_ACCEPT
ROUTE_TYPE_REJECT
)
func (t RouteType) ToApiStruct() api.RouteAction {
switch t {
case ROUTE_TYPE_ACCEPT:
return api.RouteAction_ACCEPT
case ROUTE_TYPE_REJECT:
return api.RouteAction_REJECT
}
return api.RouteAction_NONE
}
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 toConfigMatchSetOption(a api.MatchType) (config.MatchSetOptionsType, error) {
var typ config.MatchSetOptionsType
switch a {
case api.MatchType_ANY:
typ = config.MATCH_SET_OPTIONS_TYPE_ANY
case api.MatchType_ALL:
typ = config.MATCH_SET_OPTIONS_TYPE_ALL
case api.MatchType_INVERT:
typ = config.MATCH_SET_OPTIONS_TYPE_INVERT
default:
return typ, fmt.Errorf("invalid match type")
}
return typ, nil
}
func toConfigMatchSetOptionRestricted(a api.MatchType) (config.MatchSetOptionsRestrictedType, error) {
var typ config.MatchSetOptionsRestrictedType
switch a {
case api.MatchType_ANY:
typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_ANY
case api.MatchType_INVERT:
typ = config.MATCH_SET_OPTIONS_RESTRICTED_TYPE_INVERT
default:
return typ, fmt.Errorf("invalid match type")
}
return typ, nil
}
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
)
type ActionType int
const (
ACTION_ROUTING ActionType = iota
ACTION_COMMUNITY
ACTION_EXT_COMMUNITY
ACTION_MED
ACTION_AS_PATH_PREPEND
)
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
)
const (
ASPATH_REGEXP_MAGIC = "(^|[,{}() ]|$)"
)
type DefinedSet interface {
Type() DefinedType
Name() string
ToApiStruct() *api.DefinedSet
Append(DefinedSet) error
Remove(DefinedSet) error
Replace(DefinedSet) error
}
type DefinedSetMap map[DefinedType]map[string]DefinedSet
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) ToApiStruct() *api.Prefix {
return &api.Prefix{
IpPrefix: p.Prefix.String(),
MaskLengthMin: uint32(p.MasklengthRangeMin),
MaskLengthMax: uint32(p.MasklengthRangeMax),
}
}
func NewPrefixFromApiStruct(a *api.Prefix) (*Prefix, error) {
addr, prefix, err := net.ParseCIDR(a.IpPrefix)
if err != nil {
return nil, err
}
rf := bgp.RF_IPv4_UC
if addr.To4() == nil {
rf = bgp.RF_IPv6_UC
}
return &Prefix{
Prefix: prefix,
AddressFamily: rf,
MasklengthRangeMin: uint8(a.MaskLengthMin),
MasklengthRangeMax: uint8(a.MaskLengthMax),
}, nil
}
func NewPrefix(c config.Prefix) (*Prefix, error) {
addr, prefix, err := net.ParseCIDR(c.IpPrefix)
if err != nil {
return nil, err
}
rf := bgp.RF_IPv4_UC
if addr.To4() == nil {
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
}
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")
}
rhs.tree.Walk(func(s string, v interface{}) bool {
lhs.tree.Insert(s, v)
return false
})
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(s string, v interface{}) bool {
lhs.tree.Delete(s)
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
return nil
}
func (s *PrefixSet) ToApiStruct() *api.DefinedSet {
list := make([]*api.Prefix, 0, s.tree.Len())
s.tree.Walk(func(s string, v interface{}) bool {
list = append(list, v.(*Prefix).ToApiStruct())
return false
})
return &api.DefinedSet{
Type: api.DefinedType(s.Type()),
Name: s.name,
Prefixes: list,
}
}
func NewPrefixSetFromApiStruct(a *api.DefinedSet) (*PrefixSet, error) {
if a.Name == "" {
return nil, fmt.Errorf("empty prefix set name")
}
tree := radix.New()
for _, x := range a.Prefixes {
y, err := NewPrefixFromApiStruct(x)
if err != nil {
return nil, err
}
tree.Insert(CidrToRadixkey(y.Prefix.String()), y)
}
return &PrefixSet{
name: a.Name,
tree: tree,
}, 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()
for _, x := range c.PrefixList {
y, err := NewPrefix(x)
if err != nil {
return nil, err
}
tree.Insert(CidrToRadixkey(y.Prefix.String()), y)
}
return &PrefixSet{
name: name,
tree: tree,
}, 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) ToApiStruct() *api.DefinedSet {
list := make([]string, 0, len(s.list))
for _, n := range s.list {
list = append(list, n.String())
}
return &api.DefinedSet{
Type: api.DefinedType(s.Type()),
Name: s.name,
List: list,
}
}
func NewNeighborSetFromApiStruct(a *api.DefinedSet) (*NeighborSet, error) {
if a.Name == "" {
return nil, fmt.Errorf("empty neighbor set name")
}
list := make([]net.IP, 0, len(a.List))
for _, x := range a.List {
addr := net.ParseIP(x)
if addr == nil {
return nil, fmt.Errorf("invalid ip address format: %s", x)
}
list = append(list, addr)
}
return &NeighborSet{
name: a.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
)
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)
}
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
}
}
return false
}
func NewSingleAsPathMatch(arg string) *singleAsPathMatch {
switch {
case len(arg) == 0:
return nil
case arg[0] == '^':
if asn, err := strconv.Atoi(arg[1:]); err == nil {
return &singleAsPathMatch{
asn: uint32(asn),
mode: LEFT_MOST,
}
}
case arg[len(arg)-1] == '$':
if asn, err := strconv.Atoi(arg[:len(arg)-1]); err == nil {
return &singleAsPathMatch{
asn: uint32(asn),
mode: ORIGIN,
}
}
default:
if asn, err := strconv.Atoi(arg); err == nil {
return &singleAsPathMatch{
asn: uint32(asn),
mode: INCLUDE,
}
}
}
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) ToApiStruct() *api.DefinedSet {
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 &api.DefinedSet{
Type: api.DefinedType(s.typ),
Name: s.name,
List: list,
}
}
func NewAsPathSetFromApiStruct(a *api.DefinedSet) (*AsPathSet, error) {
c := config.AsPathSet{
AsPathSetName: a.Name,
AsPathList: a.List,
}
return NewAsPathSet(c)
}
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
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
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 {
rhs, ok := arg.(*regExpSet)
if !ok {
return fmt.Errorf("type cast failed")
}
lhs.list = rhs.list
return nil
}
func (s *regExpSet) ToApiStruct() *api.DefinedSet {
list := make([]string, 0, len(s.list))
for _, exp := range s.list {
list = append(list, exp.String())
}
return &api.DefinedSet{
Type: api.DefinedType(s.typ),
Name: s.name,
List: list,
}
}
type CommunitySet struct {
regExpSet
}
func ParseCommunity(arg string) (uint32, error) {
i, err := strconv.Atoi(arg)
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.Atoi(arg)
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 NewCommunitySetFromApiStruct(a *api.DefinedSet) (*CommunitySet, error) {
c := config.CommunitySet{
CommunitySetName: a.Name,
CommunityList: a.List,
}
return NewCommunitySet(c)
}
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) ToApiStruct() *api.DefinedSet {
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 &api.DefinedSet{
Type: api.DefinedType(s.typ),
Name: s.name,
List: list,
}
}
func NewExtCommunitySetFromApiStruct(a *api.DefinedSet) (*ExtCommunitySet, error) {
c := config.ExtCommunitySet{
ExtCommunitySetName: a.Name,
ExtCommunityList: a.List,
}
return NewExtCommunitySet(c)
}
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 NewDefinedSetFromApiStruct(a *api.DefinedSet) (DefinedSet, error) {
switch DefinedType(a.Type) {
case DEFINED_TYPE_PREFIX:
return NewPrefixSetFromApiStruct(a)
case DEFINED_TYPE_NEIGHBOR:
return NewNeighborSetFromApiStruct(a)
case DEFINED_TYPE_AS_PATH:
return NewAsPathSetFromApiStruct(a)
case DEFINED_TYPE_COMMUNITY:
return NewCommunitySetFromApiStruct(a)
case DEFINED_TYPE_EXT_COMMUNITY:
return NewExtCommunitySetFromApiStruct(a)
default:
return nil, fmt.Errorf("invalid defined type")
}
}
type Condition interface {
Type() ConditionType
Evaluate(*Path) 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) 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]
}
switch path.GetRouteFamily() {
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
}
result := false
_, p, ok := c.set.tree.LongestPrefix(key)
if ok && p.(*Prefix).MasklengthRangeMin <= masklen && masklen <= p.(*Prefix).MasklengthRangeMax {
result = true
}
if c.option == MATCH_OPTION_INVERT {
result = !result
}
return result
}
func (c *PrefixCondition) ToApiStruct() *api.MatchSet {
return &api.MatchSet{
Type: api.MatchType(c.option),
Name: c.set.Name(),
}
}
func NewPrefixConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*PrefixCondition, error) {
if a == nil {
return nil, nil
}
typ, err := toConfigMatchSetOptionRestricted(a.Type)
if err != nil {
return nil, err
}
c := config.MatchPrefixSet{
PrefixSet: a.Name,
MatchSetOptions: typ,
}
return NewPrefixCondition(c, m)
}
func NewPrefixCondition(c config.MatchPrefixSet, m map[string]DefinedSet) (*PrefixCondition, error) {
if c.PrefixSet == "" {
return nil, nil
}
i, ok := m[c.PrefixSet]
if !ok {
return nil, fmt.Errorf("not found prefix set %s", c.PrefixSet)
}
s, ok := i.(*PrefixSet)
if !ok {
return nil, fmt.Errorf("type assert from DefinedSet to *PrefixSet failed")
}
o, err := NewMatchOption(c.MatchSetOptions)
if err != nil {
return nil, err
}
return &PrefixCondition{
set: s,
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) bool {
if len(c.set.list) == 0 {
log.Debug("NeighborList doesn't have elements")
return true
}
if path.Owner == nil {
return false
}
result := false
for _, n := range c.set.list {
if path.Owner.Equal(n) {
result = true
break
}
}
if c.option == MATCH_OPTION_INVERT {
result = !result
}
return result
}
func (c *NeighborCondition) ToApiStruct() *api.MatchSet {
return &api.MatchSet{
Type: api.MatchType(c.option),
Name: c.set.Name(),
}
}
func NewNeighborConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*NeighborCondition, error) {
if a == nil {
return nil, nil
}
typ, err := toConfigMatchSetOptionRestricted(a.Type)
if err != nil {
return nil, err
}
c := config.MatchNeighborSet{
NeighborSet: a.Name,
MatchSetOptions: typ,
}
return NewNeighborCondition(c, m)
}
func NewNeighborCondition(c config.MatchNeighborSet, m map[string]DefinedSet) (*NeighborCondition, error) {
if c.NeighborSet == "" {
return nil, nil
}
i, ok := m[c.NeighborSet]
if !ok {
return nil, fmt.Errorf("not found neighbor set %s", c.NeighborSet)
}
s, ok := i.(*NeighborSet)
if !ok {
return nil, fmt.Errorf("type assert from DefinedSet to *NeighborSet failed")
}
o, err := NewMatchOption(c.MatchSetOptions)
if err != nil {
return nil, err
}
return &NeighborCondition{
set: s,
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) ToApiStruct() *api.MatchSet {
return &api.MatchSet{
Type: api.MatchType(c.option),
Name: c.set.Name(),
}
}
func (c *AsPathCondition) Evaluate(path *Path) bool {
result := false
aspath := path.GetAsSeqList()
for _, m := range c.set.singleList {
result = m.Match(aspath)
if c.option == MATCH_OPTION_ALL && !result {
break
}
if c.option == MATCH_OPTION_ANY && result {
break
}
}
if (c.option == MATCH_OPTION_ALL && result) || (c.option == MATCH_OPTION_ANY && !result) {
aspath := path.GetAsString()
for _, r := range c.set.list {
result = r.MatchString(aspath)
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 NewAsPathConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*AsPathCondition, error) {
if a == nil {
return nil, nil
}
typ, err := toConfigMatchSetOption(a.Type)
if err != nil {
return nil, err
}
c := config.MatchAsPathSet{
AsPathSet: a.Name,
MatchSetOptions: typ,
}
return NewAsPathCondition(c, m)
}
func NewAsPathCondition(c config.MatchAsPathSet, m map[string]DefinedSet) (*AsPathCondition, error) {
if c.AsPathSet == "" {
return nil, nil
}
i, ok := m[c.AsPathSet]
if !ok {
return nil, fmt.Errorf("not found as path set %s", c.AsPathSet)
}
s, ok := i.(*AsPathSet)
if !ok {
return nil, fmt.Errorf("type assert from DefinedSet to *AsPathSet failed")
}
o, err := NewMatchOption(c.MatchSetOptions)
if err != nil {
return nil, err
}
return &AsPathCondition{
set: s,
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) ToApiStruct() *api.MatchSet {
return &api.MatchSet{
Type: api.MatchType(c.option),
Name: c.set.Name(),
}
}
func (c *CommunityCondition) Evaluate(path *Path) bool {
cs := path.GetCommunities()
result := false
for _, x := range cs {
result = false
for _, y := range c.set.list {
if y.MatchString(fmt.Sprintf("%d:%d", x>>16, x&0x0000ffff)) {
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 NewCommunityConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*CommunityCondition, error) {
if a == nil {
return nil, nil
}
typ, err := toConfigMatchSetOption(a.Type)
if err != nil {
return nil, err
}
c := config.MatchCommunitySet{
CommunitySet: a.Name,
MatchSetOptions: typ,
}
return NewCommunityCondition(c, m)
}
func NewCommunityCondition(c config.MatchCommunitySet, m map[string]DefinedSet) (*CommunityCondition, error) {
if c.CommunitySet == "" {
return nil, nil
}
i, ok := m[c.CommunitySet]
if !ok {
return nil, fmt.Errorf("not found community set %s", c.CommunitySet)
}
s, ok := i.(*CommunitySet)
if !ok {
return nil, fmt.Errorf("type assert from DefinedSet to *CommunitySet failed")
}
o, err := NewMatchOption(c.MatchSetOptions)
if err != nil {
return nil, err
}
return &CommunityCondition{
set: s,
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) ToApiStruct() *api.MatchSet {
return &api.MatchSet{
Type: api.MatchType(c.option),
Name: c.set.Name(),
}
}
func (c *ExtCommunityCondition) Evaluate(path *Path) 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 NewExtCommunityConditionFromApiStruct(a *api.MatchSet, m map[string]DefinedSet) (*ExtCommunityCondition, error) {
if a == nil {
return nil, nil
}
typ, err := toConfigMatchSetOption(a.Type)
if err != nil {
return nil, err
}
c := config.MatchExtCommunitySet{
ExtCommunitySet: a.Name,
MatchSetOptions: typ,
}
return NewExtCommunityCondition(c, m)
}
func NewExtCommunityCondition(c config.MatchExtCommunitySet, m map[string]DefinedSet) (*ExtCommunityCondition, error) {
if c.ExtCommunitySet == "" {
return nil, nil
}
i, ok := m[c.ExtCommunitySet]
if !ok {
return nil, fmt.Errorf("not found ext-community set %s", c.ExtCommunitySet)
}
s, ok := i.(*ExtCommunitySet)
if !ok {
return nil, fmt.Errorf("type assert from DefinedSet to *ExtCommunitySet failed")
}
o, err := NewMatchOption(c.MatchSetOptions)
if err != nil {
return nil, err
}
return &ExtCommunityCondition{
set: s,
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) 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) ToApiStruct() *api.AsPathLength {
return &api.AsPathLength{
Length: c.length,
Type: api.AsPathLengthType(c.operator),
}
}
func NewAsPathLengthConditionFromApiStruct(a *api.AsPathLength) (*AsPathLengthCondition, error) {
if a == nil {
return nil, nil
}
return &AsPathLengthCondition{
length: a.Length,
operator: AttributeComparison(a.Type),
}, nil
}
func NewAsPathLengthCondition(c config.AsPathLength) (*AsPathLengthCondition, error) {
if c.Value == 0 && c.Operator == "" {
return nil, nil
}
var op AttributeComparison
switch strings.ToLower(c.Operator) {
case "eq":
op = ATTRIBUTE_EQ
case "ge":
op = ATTRIBUTE_GE
case "le":
op = ATTRIBUTE_LE
default:
return nil, fmt.Errorf("invalid as path length operator: %s", c.Operator)
}
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) bool {
return c.result == path.Validation()
}
func (c *RpkiValidationCondition) Set() DefinedSet {
return nil
}
func NewRpkiValidationConditionFromApiStruct(a int32) (*RpkiValidationCondition, error) {
if a < 1 {
return nil, nil
}
var typ config.RpkiValidationResultType
typ = typ.FromInt(int(a))
return NewRpkiValidationCondition(typ)
}
func NewRpkiValidationCondition(c config.RpkiValidationResultType) (*RpkiValidationCondition, error) {
if c == config.RPKI_VALIDATION_RESULT_TYPE_NONE {
return nil, nil
}
return &RpkiValidationCondition{
result: c,
}, nil
}
type Action interface {
Type() ActionType
Apply(*Path) *Path
}
type RoutingAction struct {
AcceptRoute bool
}
func (a *RoutingAction) Type() ActionType {
return ACTION_ROUTING
}
func (a *RoutingAction) Apply(path *Path) *Path {
if a.AcceptRoute {
return path
}
return nil
}
func (a *RoutingAction) ToApiStruct() api.RouteAction {
if a.AcceptRoute {
return api.RouteAction_ACCEPT
} else {
return api.RouteAction_REJECT
}
}
func NewRoutingActionFromApiStruct(a api.RouteAction) (*RoutingAction, error) {
if a == api.RouteAction_NONE {
return nil, nil
}
accept := false
if a == api.RouteAction_ACCEPT {
accept = true
}
return &RoutingAction{
AcceptRoute: accept,
}, nil
}
func NewRoutingAction(c config.RouteDisposition) (*RoutingAction, error) {
if c.AcceptRoute == c.RejectRoute && c.AcceptRoute {
return nil, fmt.Errorf("invalid route disposition")
}
accept := false
if c.AcceptRoute && !c.RejectRoute {
accept = true
}
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 (a *CommunityAction) Type() ActionType {
return ACTION_COMMUNITY
}
func (a *CommunityAction) Apply(path *Path) *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) ToApiStruct() *api.CommunityAction {
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 &api.CommunityAction{
Type: api.CommunityActionType(a.action.ToInt()),
Communities: cs,
}
}
func NewCommunityActionFromApiStruct(a *api.CommunityAction) (*CommunityAction, error) {
if a == nil {
return nil, nil
}
var list []uint32
var removeList []*regexp.Regexp
var op config.BgpSetCommunityOptionType
op = op.FromInt(int(a.Type))
if op == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
removeList = make([]*regexp.Regexp, 0, len(a.Communities))
} else {
list = make([]uint32, 0, len(a.Communities))
}
for _, x := range a.Communities {
if op == 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: op,
list: list,
removeList: removeList,
}, nil
}
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) *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) ToApiStruct() *api.CommunityAction {
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 &api.CommunityAction{
Type: api.CommunityActionType(a.action.ToInt()),
Communities: cs,
}
}
func NewExtCommunityActionFromApiStruct(a *api.CommunityAction) (*ExtCommunityAction, error) {
if a == nil {
return nil, nil
}
var list []bgp.ExtendedCommunityInterface
var removeList []*regexp.Regexp
subtypeList := make([]bgp.ExtendedCommunityAttrSubType, 0, len(a.Communities))
var op config.BgpSetCommunityOptionType
op = op.FromInt(int(a.Type))
if op == config.BGP_SET_COMMUNITY_OPTION_TYPE_REMOVE {
removeList = make([]*regexp.Regexp, 0, len(a.Communities))
} else {
list = make([]bgp.ExtendedCommunityInterface, 0, len(a.Communities))
}
for _, x := range a.Communities {
if op == 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: op,
list: list,
removeList: removeList,
subtypeList: subtypeList,
}, nil
}
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 MedAction struct {
value int
action MedActionType
}
func (a *MedAction) Type() ActionType {
return ACTION_MED
}
func (a *MedAction) Apply(path *Path) *Path {
var err error
switch a.action {
case MED_ACTION_MOD:
err = path.SetMed(int64(a.value), false)
case MED_ACTION_REPLACE:
err = path.SetMed(int64(a.value), true)
}
if err != nil {
log.WithFields(log.Fields{
"Topic": "Policy",
"Type": "Med Action",
}).Warn(err)
}
return path
}
func (a *MedAction) ToApiStruct() *api.MedAction {
return &api.MedAction{
Type: api.MedActionType(a.action),
Value: int64(a.value),
}
}
func NewMedActionFromApiStruct(a *api.MedAction) (*MedAction, error) {
if a == nil {
return nil, nil
}
return &MedAction{
action: MedActionType(a.Type),
value: int(a.Value),
}, nil
}
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.Atoi(string(c))
return &MedAction{
value: value,
action: action,
}, 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) *Path {
var asn uint32
if a.useLeftMost {
aspath := path.GetAsSeqList()
if len(aspath) == 0 {
log.WithFields(log.Fields{
"Topic": "Policy",
"Type": "AsPathPrepend Action",
}).Warnf("aspath length is zero.")
return path
}
asn = aspath[0]
if asn == 0 {
log.WithFields(log.Fields{
"Topic": "Policy",
"Type": "AsPathPrepend Action",
}).Warnf("left-most ASN is not seq")
return path
}
} else {
asn = a.asn
}
path.PrependAsn(asn, a.repeat)
return path
}
func (a *AsPathPrependAction) ToApiStruct() *api.AsPrependAction {
return &api.AsPrependAction{
Asn: a.asn,
Repeat: uint32(a.repeat),
UseLeftMost: a.useLeftMost,
}
}
func NewAsPathPrependActionFromApiStruct(a *api.AsPrependAction) (*AsPathPrependAction, error) {
if a == nil {
return nil, nil
}
return &AsPathPrependAction{
asn: a.Asn,
useLeftMost: a.UseLeftMost,
repeat: uint8(a.Repeat),
}, nil
}
// 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.Atoi(action.As)
if err != nil {
return nil, fmt.Errorf("As number string invalid")
}
a.asn = uint32(asn)
}
return a, 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) bool {
for _, c := range s.Conditions {
if !c.Evaluate(p) {
return false
}
}
return true
}
func (s *Statement) Apply(path *Path) (RouteType, *Path) {
result := s.Evaluate(path)
if result {
if len(s.ModActions) != 0 {
// apply all modification actions
path = path.Clone(path.Owner, path.IsWithdraw)
for _, action := range s.ModActions {
path = action.Apply(path)
}
}
//Routing action
if s.RouteAction == nil || reflect.ValueOf(s.RouteAction).IsNil() {
log.WithFields(log.Fields{
"Topic": "Policy",
"Path": path,
"PolicyName": s.Name,
}).Warn("route action is nil")
return ROUTE_TYPE_NONE, path
}
p := s.RouteAction.Apply(path)
if p == nil {
return ROUTE_TYPE_REJECT, path
}
return ROUTE_TYPE_ACCEPT, path
}
return ROUTE_TYPE_NONE, path
}
func (s *Statement) ToApiStruct() *api.Statement {
cs := &api.Conditions{}
for _, c := range s.Conditions {
switch c.(type) {
case *PrefixCondition:
cs.PrefixSet = c.(*PrefixCondition).ToApiStruct()
case *NeighborCondition:
cs.NeighborSet = c.(*NeighborCondition).ToApiStruct()
case *AsPathLengthCondition:
cs.AsPathLength = c.(*AsPathLengthCondition).ToApiStruct()
case *AsPathCondition:
cs.AsPathSet = c.(*AsPathCondition).ToApiStruct()
case *CommunityCondition:
cs.CommunitySet = c.(*CommunityCondition).ToApiStruct()
case *ExtCommunityCondition:
cs.ExtCommunitySet = c.(*ExtCommunityCondition).ToApiStruct()
case *RpkiValidationCondition:
cs.RpkiResult = int32(c.(*RpkiValidationCondition).result.ToInt())
}
}
as := &api.Actions{}
if s.RouteAction != nil && !reflect.ValueOf(s.RouteAction).IsNil() {
as.RouteAction = s.RouteAction.(*RoutingAction).ToApiStruct()
}
for _, a := range s.ModActions {
switch a.(type) {
case *CommunityAction:
as.Community = a.(*CommunityAction).ToApiStruct()
case *MedAction:
as.Med = a.(*MedAction).ToApiStruct()
case *AsPathPrependAction:
as.AsPrepend = a.(*AsPathPrependAction).ToApiStruct()
case *ExtCommunityAction:
as.ExtCommunity = a.(*ExtCommunityAction).ToApiStruct()
}
}
return &api.Statement{
Name: s.Name,
Conditions: cs,
Actions: as,
}
}
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 NewStatementFromApiStruct(a *api.Statement, dmap DefinedSetMap) (*Statement, error) {
if a.Name == "" {
return nil, fmt.Errorf("empty statement name")
}
var ra Action
var as []Action
var cs []Condition
var err error
if a.Conditions != nil {
cfs := []func() (Condition, error){
func() (Condition, error) {
return NewPrefixConditionFromApiStruct(a.Conditions.PrefixSet, dmap[DEFINED_TYPE_PREFIX])
},
func() (Condition, error) {
return NewNeighborConditionFromApiStruct(a.Conditions.NeighborSet, dmap[DEFINED_TYPE_NEIGHBOR])
},
func() (Condition, error) {
return NewAsPathLengthConditionFromApiStruct(a.Conditions.AsPathLength)
},
func() (Condition, error) {
return NewRpkiValidationConditionFromApiStruct(a.Conditions.RpkiResult)
},
func() (Condition, error) {
return NewAsPathConditionFromApiStruct(a.Conditions.AsPathSet, dmap[DEFINED_TYPE_AS_PATH])
},
func() (Condition, error) {
return NewCommunityConditionFromApiStruct(a.Conditions.CommunitySet, dmap[DEFINED_TYPE_COMMUNITY])
},
func() (Condition, error) {
return NewExtCommunityConditionFromApiStruct(a.Conditions.ExtCommunitySet, dmap[DEFINED_TYPE_EXT_COMMUNITY])
},
}
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)
}
}
}
if a.Actions != nil {
ra, err = NewRoutingActionFromApiStruct(a.Actions.RouteAction)
if err != nil {
return nil, err
}
afs := []func() (Action, error){
func() (Action, error) {
return NewCommunityActionFromApiStruct(a.Actions.Community)
},
func() (Action, error) {
return NewExtCommunityActionFromApiStruct(a.Actions.ExtCommunity)
},
func() (Action, error) {
return NewMedActionFromApiStruct(a.Actions.Med)
},
func() (Action, error) {
return NewAsPathPrependActionFromApiStruct(a.Actions.AsPrepend)
},
}
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: a.Name,
Conditions: cs,
RouteAction: ra,
ModActions: as,
}, nil
}
func NewStatement(c config.Statement, dmap DefinedSetMap) (*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, dmap[DEFINED_TYPE_PREFIX])
},
func() (Condition, error) {
return NewNeighborCondition(c.Conditions.MatchNeighborSet, dmap[DEFINED_TYPE_NEIGHBOR])
},
func() (Condition, error) {
return NewAsPathLengthCondition(c.Conditions.BgpConditions.AsPathLength)
},
func() (Condition, error) {
return NewRpkiValidationCondition(c.Conditions.BgpConditions.RpkiValidationResult)
},
func() (Condition, error) {
return NewAsPathCondition(c.Conditions.BgpConditions.MatchAsPathSet, dmap[DEFINED_TYPE_AS_PATH])
},
func() (Condition, error) {
return NewCommunityCondition(c.Conditions.BgpConditions.MatchCommunitySet, dmap[DEFINED_TYPE_COMMUNITY])
},
func() (Condition, error) {
return NewExtCommunityCondition(c.Conditions.BgpConditions.MatchExtCommunitySet, dmap[DEFINED_TYPE_EXT_COMMUNITY])
},
}
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 NewMedAction(c.Actions.BgpActions.SetMed)
},
func() (Action, error) {
return NewAsPathPrependAction(c.Actions.BgpActions.SetAsPathPrepend)
},
}
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
}
func (p *Policy) Name() string {
return p.name
}
// 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) (RouteType, *Path) {
for _, stmt := range p.Statements {
var result RouteType
result, path = stmt.Apply(path)
if result != ROUTE_TYPE_NONE {
return result, path
}
}
return ROUTE_TYPE_NONE, path
}
func (p *Policy) ToApiStruct() *api.Policy {
ss := make([]*api.Statement, 0, len(p.Statements))
for _, s := range p.Statements {
ss = append(ss, s.ToApiStruct())
}
return &api.Policy{
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)-len(rhs.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 NewPolicyFromApiStruct(a *api.Policy, dmap DefinedSetMap) (*Policy, error) {
if a.Name == "" {
return nil, fmt.Errorf("empty policy name")
}
stmts := make([]*Statement, 0, len(a.Statements))
for idx, x := range a.Statements {
if x.Name == "" {
x.Name = fmt.Sprintf("%s_stmt%d", a.Name, idx)
}
y, err := NewStatementFromApiStruct(x, dmap)
if err != nil {
return nil, err
}
stmts = append(stmts, y)
}
return &Policy{
name: a.Name,
Statements: stmts,
}, nil
}
func NewPolicy(c config.PolicyDefinition, dmap DefinedSetMap) (*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, dmap)
if err != nil {
return nil, err
}
st = append(st, s)
}
}
return &Policy{
name: c.Name,
Statements: st,
}, nil
}
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
}
func (r *RoutingPolicy) ApplyPolicy(id string, dir PolicyDirection, before *Path) *Path {
if before == nil {
return nil
}
if filtered := before.Filtered(id); filtered > POLICY_DIRECTION_NONE && filtered < dir {
return nil
}
result := ROUTE_TYPE_NONE
after := before
for _, p := range r.GetPolicy(id, dir) {
result, after = p.Apply(before)
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))
for _, name := range names {
p, ok := r.PolicyMap[name]
if !ok {
return nil, def, fmt.Errorf("not found policy %s", name)
}
ps = append(ps, p)
}
return ps, def, 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
}
pmap := make(map[string]*Policy)
smap := make(map[string]*Statement)
for _, x := range c.PolicyDefinitions {
y, err := NewPolicy(x, dmap)
if err != nil {
return err
}
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
}
}
r.DefinedSetMap = dmap
r.PolicyMap = pmap
r.StatementMap = smap
r.AssignmentMap = make(map[string]*Assignment)
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, map[string]DefinedSet{v.Name: set})
return c.Evaluate(path)
}
func PoliciesToString(ps []*api.Policy) []string {
names := make([]string, 0, len(ps))
for _, p := range ps {
names = append(names, p.Name)
}
return names
}