summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packet/bgp.go37
-rw-r--r--policy/policy.go204
-rw-r--r--table/path.go11
3 files changed, 252 insertions, 0 deletions
diff --git a/packet/bgp.go b/packet/bgp.go
index b95d48f3..3ff5615e 100644
--- a/packet/bgp.go
+++ b/packet/bgp.go
@@ -3023,6 +3023,7 @@ func NewPathAttributeMpUnreachNLRI(nlri []AddrPrefixInterface) *PathAttributeMpU
type ExtendedCommunityInterface interface {
Serialize() ([]byte, error)
String() string
+ GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType)
ToApiStruct() *api.ExtendedCommunity
}
@@ -3050,6 +3051,14 @@ func (e *TwoOctetAsSpecificExtended) String() string {
return fmt.Sprintf("%d:%d", e.AS, e.LocalAdmin)
}
+func (e *TwoOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ t := EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
+ if !e.IsTransitive {
+ t = EC_TYPE_NON_TRANSITIVE_TWO_OCTET_AS_SPECIFIC
+ }
+ return t, e.SubType
+}
+
func (e *TwoOctetAsSpecificExtended) ToApiStruct() *api.ExtendedCommunity {
return &api.ExtendedCommunity{
Type: api.EXTENDED_COMMUNITIE_TYPE_TWO_OCTET_AS_SPECIFIC,
@@ -3093,6 +3102,14 @@ func (e *IPv4AddressSpecificExtended) String() string {
return fmt.Sprintf("%s:%d", e.IPv4.String(), e.LocalAdmin)
}
+func (e *IPv4AddressSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ t := EC_TYPE_TRANSITIVE_IP4_SPECIFIC
+ if !e.IsTransitive {
+ t = EC_TYPE_NON_TRANSITIVE_IP4_SPECIFIC
+ }
+ return t, e.SubType
+}
+
func (e *IPv4AddressSpecificExtended) ToApiStruct() *api.ExtendedCommunity {
return &api.ExtendedCommunity{
Type: api.EXTENDED_COMMUNITIE_TYPE_IP4_SPECIFIC,
@@ -3131,6 +3148,14 @@ func (e *FourOctetAsSpecificExtended) String() string {
return fmt.Sprintf("%d.%d:%d", asUpper, asLower, e.LocalAdmin)
}
+func (e *FourOctetAsSpecificExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ t := EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
+ if !e.IsTransitive {
+ t = EC_TYPE_NON_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC
+ }
+ return t, e.SubType
+}
+
func (e *FourOctetAsSpecificExtended) ToApiStruct() *api.ExtendedCommunity {
return &api.ExtendedCommunity{
Type: api.EXTENDED_COMMUNITIE_TYPE_FOUR_OCTET_AS_SPECIFIC,
@@ -3259,6 +3284,14 @@ func (e *OpaqueExtended) String() string {
return e.Value.String()
}
+func (e *OpaqueExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ t := EC_TYPE_TRANSITIVE_OPAQUE
+ if !e.IsTransitive {
+ t = EC_TYPE_NON_TRANSITIVE_OPAQUE
+ }
+ return t, ExtendedCommunityAttrSubType(0xFF)
+}
+
func (e *OpaqueExtended) ToApiStruct() *api.ExtendedCommunity {
return &api.ExtendedCommunity{
Type: api.EXTENDED_COMMUNITIE_TYPE_OPAQUE,
@@ -3290,6 +3323,10 @@ func (e *UnknownExtended) String() string {
return fmt.Sprintf("%d", v)
}
+func (e *UnknownExtended) GetTypes() (ExtendedCommunityAttrType, ExtendedCommunityAttrSubType) {
+ return ExtendedCommunityAttrType(0xFF), ExtendedCommunityAttrSubType(0xFF)
+}
+
func (e *UnknownExtended) ToApiStruct() *api.ExtendedCommunity {
return &api.ExtendedCommunity{}
}
diff --git a/policy/policy.go b/policy/policy.go
index 300fcadf..b35e765f 100644
--- a/policy/policy.go
+++ b/policy/policy.go
@@ -103,6 +103,13 @@ func NewPolicy(pd config.PolicyDefinition, ds config.DefinedSets) *Policy {
conditions = append(conditions, cc)
}
+ // ExtendedCommunityCondition
+ extCommunitySetName := statement.Conditions.BgpConditions.MatchExtCommunitySet
+ ecc := NewExtCommunityCondition(extCommunitySetName, ds.BgpDefinedSets.ExtCommunitySetList)
+ if ecc != nil {
+ conditions = append(conditions, ecc)
+ }
+
// routing action
ra := NewRoutingAction(statement.Actions)
@@ -689,6 +696,203 @@ func (c *CommunityCondition) evaluate(path *table.Path) bool {
return false
}
+type ExtCommunityCondition struct {
+ DefaultCondition
+ ExtCommunityList []*ExtCommunityElement
+}
+
+type ExtCommunityElement struct {
+ ecType bgp.ExtendedCommunityAttrType
+ ecSubType bgp.ExtendedCommunityAttrSubType
+ globalAdmin interface{}
+ localAdmin uint32
+ comStr string
+ isRegExp bool
+ regExp *regexp.Regexp
+}
+
+func NewExtCommunityCondition(extComSetName string, defExtComSetList []config.ExtCommunitySet) *ExtCommunityCondition {
+ extCommunityList := make([]*ExtCommunityElement, 0)
+ for _, extComSet := range defExtComSetList {
+ if extComSet.ExtCommunitySetName == extComSetName {
+ for _, c := range extComSet.ExtCommunityMembers {
+ matchAll := false
+ e := &ExtCommunityElement{
+ isRegExp: false,
+ }
+ matchType, val := getECommunitySubType(c)
+ if !matchType {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Condition",
+ }).Error("failed to parse the sub type %s.", c)
+ return nil
+ }
+ switch val[1] {
+ case "RT":
+ e.ecSubType = bgp.EC_SUBTYPE_ROUTE_TARGET
+ case "SoO":
+ e.ecSubType = bgp.EC_SUBTYPE_ROUTE_ORIGIN
+ default:
+ e.ecSubType = bgp.ExtendedCommunityAttrSubType(0xFF)
+ }
+
+ if matchVal, elem := getECommunityValue(val[2]); matchVal {
+ if matchElem, ecType, gAdmin := getECommunityElem(elem[1]); matchElem {
+ e.ecType = ecType
+ e.globalAdmin = gAdmin
+ lAdmin, err := strconv.ParseUint(elem[2], 10, 32)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Extended Community Condition",
+ }).Errorf("failed to parse the local administrator %d.", elem[2])
+ return nil
+ }
+ e.localAdmin = uint32(lAdmin)
+ matchAll = true
+ }
+ }
+ if !matchAll{
+ e.isRegExp = true
+ reg, err := regexp.Compile(val[2])
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Community Condition",
+ }).Errorf("Regular expression can't be compiled %d.", val[2])
+ return nil
+ }
+ e.regExp = reg
+ }
+ extCommunityList = append(extCommunityList, e)
+ }
+ ce := &ExtCommunityCondition{
+ ExtCommunityList: extCommunityList,
+ }
+ return ce
+ }
+ }
+ return nil
+}
+
+func getECommunitySubType(eComStr string) (bool, []string) {
+ regSubType, _ := regexp.Compile("^(RT|SoO):(.*)$")
+ if regSubType.MatchString(eComStr) {
+ eComVal := regSubType.FindStringSubmatch(eComStr)
+ return true, eComVal
+ }
+ return false, nil
+}
+
+func getECommunityValue(eComVal string) (bool, []string) {
+ regVal, _ := regexp.Compile("^([0-9\\.]+):([0-9]+)$")
+ if regVal.MatchString(eComVal) {
+ eComElem := regVal.FindStringSubmatch(eComVal)
+ return true, eComElem
+ }
+ return false, nil
+}
+
+func getECommunityElem(gAdmin string) (bool, bgp.ExtendedCommunityAttrType, interface{}) {
+ addr := net.ParseIP(gAdmin)
+ if addr.To4() != nil {
+ return true, bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC, addr
+ }
+ regAs, _ := regexp.Compile("^([0-9]+)$")
+ if regAs.MatchString(gAdmin) {
+ as, err := strconv.ParseUint(gAdmin, 10, 16)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Extended Community Condition",
+ }).Errorf("failed to parse the global administrator %d.", gAdmin)
+ }
+ return true, bgp.EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC, uint16(as)
+ }
+ regAs4, _ := regexp.Compile("^([0-9]+).([0-9]+)$")
+ if regAs4.MatchString(gAdmin) {
+ as4Elem := regAs4.FindStringSubmatch(gAdmin)
+ highAs, errHigh := strconv.ParseUint(as4Elem[1], 10, 16)
+ lowAs, errLow := strconv.ParseUint(as4Elem[2], 10, 16)
+ if errHigh != nil || errLow != nil {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Type": "Extended Community Condition",
+ }).Errorf("failed to parse the global administrator %d.", gAdmin)
+ }
+ return true, bgp.EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC, uint32(highAs<<16 | lowAs)
+ }
+ return false, bgp.ExtendedCommunityAttrType(0xFF), nil
+}
+
+// compare extended community in the message's attribute with
+// the one in the condition.
+func (c *ExtCommunityCondition) evaluate(path *table.Path) bool {
+
+ makeStr := func(ec *ExtCommunityElement) string {
+ t := ec.ecType
+ str := fmt.Sprintf("%d", ec.localAdmin)
+ switch t {
+ case bgp.EC_TYPE_TRANSITIVE_TWO_OCTET_AS_SPECIFIC:
+ str = fmt.Sprintf("%d:%d", ec.globalAdmin.(uint16), str)
+ case bgp.EC_TYPE_TRANSITIVE_IP4_SPECIFIC:
+ str = fmt.Sprintf("%s:%d", ec.globalAdmin.(net.IP).String(), str)
+ case bgp.EC_TYPE_TRANSITIVE_FOUR_OCTET_AS_SPECIFIC:
+ ga := ec.globalAdmin.(uint32)
+ upper := strconv.FormatUint(uint64(ga&0xFFFF0000>>16), 10)
+ lower := strconv.FormatUint(uint64(ga&0x0000FFFF), 10)
+ str = fmt.Sprintf("%d.%d:%s", upper, lower, str)
+ }
+ return str
+ }
+
+ eCommunities := path.GetExtCommunities()
+ if len(eCommunities) == 0 {
+ return false
+ }
+
+ matched := false
+ matchStr := ""
+
+ for _, member := range c.ExtCommunityList {
+ for _, eCommunity := range eCommunities {
+ ec := eCommunity.(bgp.ExtendedCommunityInterface)
+ t, st := ec.GetTypes()
+ if member.ecType != t || member.ecSubType != st {
+ continue
+ }
+ if member.isRegExp {
+ if member.regExp.MatchString(ec.String()) {
+ matched = true
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "RegExp": member.regExp.String(),
+ }).Debug("extended community regexp used")
+ matchStr = ec.String()
+ break
+ }
+ } else {
+ if makeStr(member) == ec.String() {
+ matched = true
+ matchStr = ec.String()
+ break
+ }
+ }
+ }
+ if matched {
+ log.WithFields(log.Fields{
+ "Topic": "Policy",
+ "Condition": "Community",
+ "Community": matchStr,
+ }).Debug("condition matched")
+
+ return true
+ }
+ }
+ return false
+}
+
type Action interface {
apply(*table.Path) *table.Path
}
diff --git a/table/path.go b/table/path.go
index 302847f8..75c67a83 100644
--- a/table/path.go
+++ b/table/path.go
@@ -438,6 +438,17 @@ func (path *Path) ClearCommunities() {
}
}
+func (path *Path) GetExtCommunities() []interface{} {
+ eCommunityList := make([]interface{}, 0)
+ if _, attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_EXTENDED_COMMUNITIES); attr != nil {
+ eCommunities := attr.(*bgp.PathAttributeExtendedCommunities).Value
+ for _, eCommunity := range eCommunities {
+ eCommunityList = append(eCommunityList, eCommunity)
+ }
+ }
+ return eCommunityList
+}
+
func (path *Path) GetMed() (uint32, error) {
_, attr := path.getPathAttr(bgp.BGP_ATTR_TYPE_MULTI_EXIT_DISC)
if attr == nil {